本小节展示了如何使用简单的或双向的TLS来暴露一个安全HTTPS服务。

1. 准备工作

  1. 部署httpbin服务
  1. # 开启自动sidecar注入的话,执行如下命令
  2. $ kubectl apply -f samples/httpbin/httpbin.yaml
  3. # 如果没有开启的话,需要手工注入的话执行如下
  4. $ kubectl apply -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml)

[warning] 注意,使用curl进行模拟的时候,应该有LibreSSL库的支持。要不然后面做双向TLS时会报错。

2. 确定Ingress IP和 ports

  1. $ kubectl get svc istio-ingressgateway -n istio-system
  2. $ export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
  3. $ export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].port}')
  4. $ export SECURE_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].port}')
  5. $ export TCP_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="tcp")].port}')

3. 产生客户端和服务器的证书及私钥

  1. 创建一个根证书和私有key, 用它来对服务签署证书
  1. $ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example.com.key -out example.com.crt
  1. 针对httpbin.example.com 创建证书和私有key.
  1. $ openssl req -out httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"
  2. $ openssl x509 -req -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in httpbin.example.com.csr -out httpbin.example.com.crt

4. 针对单一的主机配置TLS ingress gateway

  1. 针对ingress gateway创建一个secret
  1. $ kubectl create -n istio-system secret tls httpbin-credential --key=httpbin.example.com.key --cert=httpbin.example.com.crt
  1. 针对servers定义gateway,指定端口为443, 并且针对credentialName的值指向httpbin-credential. 这个值和secret's的名称是一样的。 TLS的模式是SIMPLE
  1. $ cat <<EOF | kubectl apply -f -
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: Gateway
  4. metadata:
  5. name: mygateway
  6. spec:
  7. selector:
  8. istio: ingressgateway # use istio default ingress gateway
  9. servers:
  10. - port:
  11. number: 443
  12. name: https
  13. protocol: HTTPS
  14. tls:
  15. mode: SIMPLE
  16. credentialName: httpbin-credential # must be the same as secret
  17. hosts:
  18. - httpbin.example.com
  19. EOF
  1. 配置gateway’s的ingress traffic路由。 定义相对应的虚拟服务
  1. $ cat <<EOF | kubectl apply -f -
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: VirtualService
  4. metadata:
  5. name: httpbin
  6. spec:
  7. hosts:
  8. - "httpbin.example.com"
  9. gateways:
  10. - mygateway
  11. http:
  12. - match:
  13. - uri:
  14. prefix: /status
  15. - uri:
  16. prefix: /delay
  17. route:
  18. - destination:
  19. port:
  20. number: 8000
  21. host: httpbin
  22. EOF
  1. 发送HTTPS请求,通过HTTPS来访问httpbin服务
  1. $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
  2. --cacert example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
  1. 删除gateway’s的密钥,和创建一个新的去改变ingress gateway’s的凭据。
  1. $ kubectl -n istio-system delete secret httpbin-credential
  1. $ mkdir new_certificates
  2. $ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout new_certificates/example.com.key -out new_certificates/example.com.crt
  3. $ openssl req -out new_certificates/httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout new_certificates/httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"
  4. $ openssl x509 -req -days 365 -CA new_certificates/example.com.crt -CAkey new_certificates/example.com.key -set_serial 0 -in new_certificates/httpbin.example.com.csr -out new_certificates/httpbin.example.com.crt
  5. $ kubectl create -n istio-system secret tls httpbin-credential \
  6. --key=new_certificates/httpbin.example.com.key \
  7. --cert=new_certificates/httpbin.example.com.crt
  1. 通过curl来使用新的证书链访问httpbin服务
  1. curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
  2. --cacert new_certificates/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
  1. 假如你尝试使用先前的证书去访问,那么将会失败。
  1. $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
  2. --cacert example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"

5. 针对多个主机配置TLS ingress gateway

你可以针对多个主机配置ingress gateway,比如针对httpbin.example.comhelloworld-v1.example.com两个主机。ingress gateway根据相对应的credentialName检索唯一的凭据名称。

  1. 为了还原针对httpbin的凭据,删除它的密钥,然后再创建它。
  1. $ kubectl -n istio-system delete secret httpbin-credential
  2. $ kubectl create -n istio-system secret tls httpbin-credential \
  3. --key=httpbin.example.com.key \
  4. --cert=httpbin.example.com.crt
  1. 启动helloworld-v1案例
  1. cat <<EOF | kubectl apply -f -
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5. name: helloworld-v1
  6. labels:
  7. app: helloworld-v1
  8. spec:
  9. ports:
  10. - name: http
  11. port: 5000
  12. selector:
  13. app: helloworld-v1
  14. ---
  15. apiVersion: apps/v1
  16. kind: Deployment
  17. metadata:
  18. name: helloworld-v1
  19. spec:
  20. replicas: 1
  21. selector:
  22. matchLabels:
  23. app: helloworld-v1
  24. version: v1
  25. template:
  26. metadata:
  27. labels:
  28. app: helloworld-v1
  29. version: v1
  30. spec:
  31. containers:
  32. - name: helloworld
  33. image: istio/examples-helloworld-v1
  34. resources:
  35. requests:
  36. cpu: "100m"
  37. imagePullPolicy: IfNotPresent #Always
  38. ports:
  39. - containerPort: 5000
  40. EOF
  1. 针对helloworld-v1.example.com 产生证书和私有key。
  1. $ openssl req -out helloworld-v1.example.com.csr -newkey rsa:2048 -nodes -keyout helloworld-v1.example.com.key -subj "/CN=helloworld-v1.example.com/O=helloworld organization"
  2. $ openssl x509 -req -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 1 -in helloworld-v1.example.com.csr -out helloworld-v1.example.com.crt
  1. 创建helloworld-credential 密钥
  1. $ kubectl create -n istio-system secret tls helloworld-credential --key=helloworld-v1.example.com.key --cert=helloworld-v1.example.com.crt
  1. 定义gateway如下 所示:
  1. $ cat <<EOF | kubectl apply -f -
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: Gateway
  4. metadata:
  5. name: mygateway
  6. spec:
  7. selector:
  8. istio: ingressgateway # use istio default ingress gateway
  9. servers:
  10. - port:
  11. number: 443
  12. name: https-httpbin
  13. protocol: HTTPS
  14. tls:
  15. mode: SIMPLE
  16. credentialName: httpbin-credential
  17. hosts:
  18. - httpbin.example.com
  19. - port:
  20. number: 443
  21. name: https-helloworld
  22. protocol: HTTPS
  23. tls:
  24. mode: SIMPLE
  25. credentialName: helloworld-credential
  26. hosts:
  27. - helloworld-v1.example.com
  28. EOF
  1. 定义gateway的流量路由,定义相对应的虚拟服务
  1. $ cat <<EOF | kubectl apply -f -
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: VirtualService
  4. metadata:
  5. name: helloworld-v1
  6. spec:
  7. hosts:
  8. - helloworld-v1.example.com
  9. gateways:
  10. - mygateway
  11. http:
  12. - match:
  13. - uri:
  14. exact: /hello
  15. route:
  16. - destination:
  17. host: helloworld-v1
  18. port:
  19. number: 5000
  20. EOF
  1. 发送https的请求到helloworld-v1.example.com
  1. $ curl -v -HHost:helloworld-v1.example.com --resolve "helloworld-v1.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
  2. --cacert example.com.crt "https://helloworld-v1.example.com:$SECURE_INGRESS_PORT/hello"
  1. 发送https请求到httpbin.example.com去测试。
  1. curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
  2. --cacert example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"

6. 配置双向的TLS ingress gateway

可以扩展你gateway’s的定义去支持双向的TLS. 改变ingress gateway可以通过删除它的secret,再重新创建一个来实现。服务器使用CA证书去校验它的客户端,因此我们还要加载CA证书。

  1. $ kubectl -n istio-system delete secret httpbin-credential
  2. $ kubectl create -n istio-system secret generic httpbin-credential --from-file=tls.key=httpbin.example.com.key \
  3. --from-file=tls.crt=httpbin.example.com.crt --from-file=ca.crt=example.com.crt
  1. 改变gateway的定义,设置TLS的模式为MUTUAL.
  1. $ cat <<EOF | kubectl apply -f -
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: Gateway
  4. metadata:
  5. name: mygateway
  6. spec:
  7. selector:
  8. istio: ingressgateway # use istio default ingress gateway
  9. servers:
  10. - port:
  11. number: 443
  12. name: https
  13. protocol: HTTPS
  14. tls:
  15. mode: MUTUAL
  16. credentialName: httpbin-credential # must be the same as secret
  17. hosts:
  18. - httpbin.example.com
  19. EOF
  1. 尝试使用先前的方法去发送HTTPS请求,来看它是如何失败的
  1. $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
  2. --cacert example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
  1. 产生客户端的证书和私有key
  1. $ openssl req -out client.example.com.csr -newkey rsa:2048 -nodes -keyout client.example.com.key -subj "/CN=client.example.com/O=client organization"
  2. $ openssl x509 -req -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 1 -in client.example.com.csr -out client.example.com.crt
  1. 传送client的证书和key给curl和重新发送请求。
  1. $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
  2. --cacert ./example.com.crt --cert ./client.example.com.crt --key ./client.example.com.key \
  3. "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"

7. 排错

  1. 检查INGRESS_HOST和SECURE_INGRESS_PORT的环境变量值。
  1. $ kubectl get svc -n istio-system
  2. $ echo "INGRESS_HOST=$INGRESS_HOST, SECURE_INGRESS_PORT=$SECURE_INGRESS_PORT"
  1. 检查istio-ingressgatewayu控制器的错误消息日志
  1. $ kubectl logs -n istio-system "$(kubectl get pod -l istio=ingressgateway \
  2. -n istio-system -o jsonpath='{.items[0].metadata.name}')"
  1. 校验你的secret是否成功的创建在了istio-system名称空间。
  1. $ kubectl -n istio-system get secrets
  1. 检查ingress gateway agent的日志是否推送key/certificate密钥对到ingress gateway.
  1. $ kubectl logs -n istio-system "$(kubectl get pod -l istio=ingressgateway \
  2. -n istio-system -o jsonpath='{.items[0].metadata.name}')"

8. 清空本课实验

  1. 删除gateway的配置,虚拟服务的定义和secrets.
  1. $ kubectl delete gateway mygateway
  2. $ kubectl delete virtualservice httpbin
  3. $ kubectl delete --ignore-not-found=true -n istio-system secret httpbin-credential \
  4. helloworld-credential
  5. $ kubectl delete --ignore-not-found=true virtualservice helloworld-v1
  1. 删除证书和keys
  1. $ rm -rf example.com.crt example.com.key httpbin.example.com.crt httpbin.example.com.key httpbin.example.com.csr helloworld-v1.example.com.crt helloworld-v1.example.com.key helloworld-v1.example.com.csr client.example.com.crt client.example.com.csr client.example.com.key ./new_certificates
  1. 关闭httpbinhelloworld-v1服务
  1. $ kubectl delete deployment --ignore-not-found=true httpbin helloworld-v1
  2. $ kubectl delete service --ignore-not-found=true httpbin helloworld-v1