官方文档:
守卫网格:配置TLS安全网关
Istio 1.5 的安全更新:
- SDS (安全发现服务)趋于稳定、默认开启
- 对等认证和请求认证配置分离
- 自动 mTLS 从 alpha 变为 beta,默认开启
- Node agent 和 Pilot agent 合并, 简化 Pod 安全策略的配置
- 支持 first-party-jwt (ServiceAccountToken) 作为 third-party-jwt 的备用
- …...
安全发现服务(SDS):
- 身份和证书管理
- 实现安全配置自动化
- 中心化 SDS Server
- 优点:
- 无需挂载 secret 卷
- 动态更新证书,无需重启
-
可监视多个证书密钥对
接下来我们配置一个安全网关,为外部提供 HTTPS 的访问方式。首先,确认 curl
命令是否通过LibreSSL去编译的:
$ curl --version |grep LibreSSL
为服务创建根证书和私钥:
$ 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
为 httpbin.example.com 域名创建证书和私钥:
$ openssl req -out httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"
$ 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
完成以上操作后,当前目录会创建如下证书和密钥文件:
[root@m1 ~]# ls |grep example
example.com.crt
example.com.key
httpbin.example.com.crt
httpbin.example.com.csr
httpbin.example.com.key
[root@m1 ~]#
部署 httpbin 服务:
apiVersion: v1
kind: Service
metadata:
name: httpbin
labels:
app: httpbin
spec:
ports:
- name: http
port: 8000
selector:
app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin
spec:
replicas: 1
selector:
matchLabels:
app: httpbin
version: v1
template:
metadata:
labels:
app: httpbin
version: v1
spec:
containers:
- image: docker.io/citizenstig/httpbin
imagePullPolicy: IfNotPresent
name: httpbin
ports:
- containerPort: 8000
然后为入口网关创建k8s的secret,将 httpbin.example.com 域名的密钥和证书挂载到secret中:
$ kubectl create -n istio-system secret tls httpbin-credential --key=httpbin.example.com.key --cert=httpbin.example.com.crt
创建入口网关,并指定外部以 https 方式访问:
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: mygateway
spec:
selector:
istio: ingressgateway
servers:
- port: # 使用https访问方式
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE # 简单模式,单向TLS
credentialName: httpbin-credential # k8s secret的名称
hosts:
- httpbin.example.com
创建虚拟服务,配置 TLS 网关和路由规则:
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
curl
测试,请求验证是否生效:
$ curl -HHost:httpbin.example.com \
--resolve httpbin.example.com:443:${INGRESS_HOST} \
--cacert example.com.crt "https://httpbin.example.com:443/status/418"
如果 istio-ingressgateway 组件是以 nodePort 方式开放端口的,那么这里的 443 端口需要替换成对应的 nodePort 端口。示例:
[root@m1 ~]# kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}'
192.168.243.140 # istio-ingressgateway 组件所在的虚拟机IP
[root@m1 ~]# kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}'
32155 # https的nodePort端口
[root@m1 ~]# curl -HHost:httpbin.example.com --resolve httpbin.example.com:32155:192.168.243.140 --cacert example.com.crt "https://httpbin.example.com:32155/status/418"
-=[ teapot ]=-
_...._
.' _ _ `.
| ."` ^ `". _,
\_;`"---"`|//
| ;/
\_ _/
`"""`
[root@m1 ~]#
配置选项:
双重保障:为应用设置不同级别的双向TLS
- 认证策略的分类
- 对等认证(PeerAuthentication)
- 请求认证(RequestAuthentication)
- 认证策略范围
- 网格
- 命名空间
- 特定服务
- 优先级:最窄原则
mTLS 简介:
- TLS:客户端根据服务端证书验证其身份
-
mTLS:客户端、服务端彼此都验证对方身份
- 对等认证主要用于服务之间的通讯,一般不去用于服务与外界的通讯,因为比较慢,双方都需要互相验证及握手
接下来我们尝试为应用设置不同级别的双向TLS。首先,创建一个用于测试的命令空间:
[root@m1 ~]# kubectl create ns testaut
namespace/testaut created
[root@m1 ~]#
在该命名空间下创建测试用的客户端(sleep):
[root@m1 ~]# kubectl apply -f /usr/local/istio-1.8.1/samples/sleep/sleep.yaml -n testaut
serviceaccount/sleep created
service/sleep created
deployment.apps/sleep created
[root@m1 ~]#
我们使用上一小节的 httpbin 服务作为服务端,注意 httpbin 是在 default 命名空间下的。我们通过 sleep 访问一下 httpbin 的接口:
[root@m1 ~]# kubectl get po -n testaut
NAME READY STATUS RESTARTS AGE
sleep-854565cb79-tk586 1/1 Running 0 2m4s
[root@m1 ~]# kubectl exec -it sleep-854565cb79-tk586 -n testaut -c sleep -- curl http://httpbin.default:8000/ip
{
"origin": "127.0.0.1"
}
[root@m1 ~]#
目前它们的通讯方式是没有采用TLS的,接下来我们配置一个对等认证策略:
$ kubectl apply -f - <<EOF
apiVersion: "security.istio.io/v1beta1"
kind: "PeerAuthentication"
metadata:
name: "default"
namespace: "default" # 给default添加命名空间策略
spec:
mtls: # 采用对等认证
mode: PERMISSIVE # 兼容模式
EOF
此时,依旧可以采用非TLS方式进行通讯,因为兼容模式可以同时通过非TLS和TLS方式进行通讯:
[root@m1 ~]# kubectl exec -it sleep-854565cb79-tk586 -n testaut -c sleep -- curl http://httpbin.default:8000/ip
{
"origin": "127.0.0.1"
}
[root@m1 ~]#
现在我们将策略改为严格模式,如下:
$ kubectl apply -f - <<EOF
apiVersion: "security.istio.io/v1beta1"
kind: "PeerAuthentication"
metadata:
name: "default"
namespace: "default"
spec:
mtls:
mode: STRICT # 严格模式
EOF
改为严格模式后,使用非TLS的通讯方式就会被拒绝访问了:
[root@m1 ~]# kubectl exec -it sleep-854565cb79-tk586 -n testaut -c sleep -- curl http://httpbin.default:8000/ip
curl: (56) Recv failure: Connection reset by peer
command terminated with exit code 56
[root@m1 ~]#
此时我们就要为网格内的服务开启自动 mTLS,开启的方式也比较简单,只需要注入 Sidecar 即可。因为 Istio 已经实现了一个自动的 mTLS ,会帮我们完成证书和密钥的管理。命令如下:
[root@m1 ~]# kubectl apply -f <(istioctl kube-inject -f /usr/local/istio-1.8.1/samples/sleep/sleep.yaml) -n testaut
serviceaccount/sleep unchanged
service/sleep unchanged
deployment.apps/sleep configured
[root@m1 ~]#
而且访问方式也不需要改变,还是和之前一样:
[root@m1 ~]# kubectl get pods -n testaut
NAME READY STATUS RESTARTS AGE
sleep-866b7dc94-dqd9p 2/2 Running 0 4m21s
[root@m1 ~]# kubectl exec -it sleep-866b7dc94-dqd9p -n testaut -c sleep -- curl http://httpbin.default:8000/ip
{
"origin": "127.0.0.1"
}
[root@m1 ~]#
上面示例的认证范围针对的是命名空间,我们也可以添加全局策略,如下示例:
$ kubectl apply -f - <<EOF
apiVersion: "security.istio.io/v1beta1"
kind: "PeerAuthentication"
metadata:
name: "default"
spec:
mtls:
mode: STRICT
EOF
配置选项:
授权策略:如何实现JWT身份认证与授权?
与认证相对应的就是授权,简单来说授权就是授予你做什么事情的权利,例如某个数据只有得到授权的用户才能访问。在 Istio 中我们可以使用 JWT 来实现身份认证与授权。
什么是 JWT:
- JWT的全称为JSON Web Token,就是JSON格式的Web令牌
- 以 JSON 格式传递信息
- 应用场景
- 授权
- 信息交换
- 组成部分
-
Header、payload、signature
-
通过如下命令创建用于测试的命名空间,以及两个分别作为客户端(sleep)和服务端(httpbin)的应用:
[root@m1 ~]# kubectl create ns testjwt
namespace/testjwt created
[root@m1 ~]# kubectl apply -f <(istioctl kube-inject -f /usr/local/istio-1.8.1/samples/httpbin/httpbin.yaml) -n testjwt # httpbin作为服务端
serviceaccount/httpbin created
service/httpbin created
deployment.apps/httpbin created
[root@m1 ~]# kubectl apply -f <(istioctl kube-inject -f /usr/local/istio-1.8.1/samples/sleep/sleep.yaml) -n testjwt # sleep作为客户端
serviceaccount/sleep created
service/sleep created
deployment.apps/sleep created
[root@m1 ~]# kubectl get pods -n testjwt
NAME READY STATUS RESTARTS AGE
httpbin-5b6477fb8-5pn4v 2/2 Running 0 48s
sleep-866b7dc94-mrzdg 2/2 Running 0 42s
[root@m1 ~]#
测试客户端与服务端之间的连通性:
[root@m1 ~]# kubectl exec "$(kubectl get pod -l app=sleep -n testjwt -o jsonpath={.items..metadata.name})" -c sleep -n testjwt -- curl http://httpbin.testjwt:8000/ip -s -o /dev/null -w "%{http_code}\n"
200
[root@m1 ~]#
接下来配置基于 JWT 的认证策略,创建一个请求认证资源,如下所示:
kubectl apply -f - <<EOF
apiVersion: "security.istio.io/v1beta1"
kind: "RequestAuthentication" # 资源类型为请求认证
metadata:
name: "jwt-example"
namespace: testjwt # 作用于哪个命名空间
spec:
selector:
matchLabels:
app: httpbin # 需要请求认证的服务
jwtRules:
- issuer: "testing@secure.istio.io" # JWT的签发人
jwks: "https://raw.githubusercontent.com/istio/istio/release-1.8/security/tools/jwt/samples/jwks.json" # 用于验证JWT签名的提供者公钥集的URL
EOF
测试使用不合法的JWT访问,会返回401:
[root@m1 ~]# kubectl exec $(kubectl get pod -l app=sleep -n testjwt -o jsonpath={.items..metadata.name}) -c sleep -n testjwt -- curl "http://httpbin.testjwt:8000/headers" -H "Authorization: Bearer invalidToken" -s -o /dev/null -w "%{http_code}\n"
401
[root@m1 ~]#
测试没有授权策略时,可以直接访问:
[root@m1 ~]# kubectl exec $(kubectl get pod -l app=sleep -n testjwt -o jsonpath={.items..metadata.name}) -c sleep -n testjwt -- curl "http://httpbin.testjwt:8000/headers" -s -o /dev/null -w "%{http_code}\n"
200
[root@m1 ~]#
配置 JWT 的授权策略,实现基于 JWT 的授权访问:
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy # 授权策略
metadata:
name: require-jwt
namespace: testjwt # 作用于哪个命名空间
spec:
selector:
matchLabels:
app: httpbin # 需要授权访问的服务
action: ALLOW # 符合授权条件时的动作,拒绝或允许
rules: # 定义授权规则
- from:
- source:
requestPrincipals: ["testing@secure.istio.io/testing@secure.istio.io"] # 来源于此JWT签发人列表的请求满足条件
EOF
解析token,并设置为系统变量:
[root@m1 ~]# TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/release-1.8/security/tools/jwt/samples/demo.jwt -s) && echo "$TOKEN" | cut -d '.' -f2 - | base64 --decode -
然后进行验证,测试带token的请求是否正常:
[root@m1 ~]# kubectl exec $(kubectl get pod -l app=sleep -n testjwt -o jsonpath={.items..metadata.name}) -c sleep -n testjwt -- curl "http://httpbin.testjwt:8000/headers" -s -o /dev/null -H "Authorization: Bearer $TOKEN" -w "%{http_code}\n"
200
[root@m1 ~]#
请求认证配置选项:
授权策略配置选项:
关于安全方面更多的内容可以参考官方文档的使用示例: