zoukankan      html  css  js  c++  java
  • istio-ingress网关安全

    传统方式下Envoy证书是通过secret卷挂载的方式以文件挂载到sidecar容器中,当证书发生轮转时需要重启服务让Envoy重新加载证书;同时证书私钥在secret中存储并在服务节点外跨节点传输的方式也存在明显的安全漏洞。为此Istio1.1版本后增加了SDS(Secret Discovery Service)API,在Citadel服务的基础上,增加了nodeagent组件,以ds的形式部署在每个节点上,而SDS服务由nodeagent管理,在每一个节点上会启动实现了SecretDiscoveryService 这个gRPC服务的SDS服务端,同时在一个节点上的sds server和client之间通过Unix domian socker通讯。nodeagent除了支持对接Citadel发送证书签发请求外,还可以对接Vault,GoogleCA等证书签发组件。

    通过如上设计给整个mesh系统带来了如下好处:
    • envoy可以动态获取轮转后的证书而无需重启
    • 安全性提升:私钥不出节点传输;证书在memory中传递而无需落盘

    sds服务请求签发证书的流程如下:

    1. Pilot将SDS相关config发送至istio sidecar,比如envoy
    2. 使用指定的serviceaccount向NodeAgent发送请求
    3. nodeagent发送CSR到citadel请求证书签发,支持自定义CA,这里同样使用sa作为访问凭证
    4. Citadel向apiserver认证sa
    5. 如果认证通过,Citadel向NodeAgent返回签发后的证书
    6. SDS返回证书内容给sidecar

    SDS安装

    通过helm方式安装 一般不需要

    helm repo add istio.io https://storage.googleapis.com/istio-release/releases/1.4.2/charts/
    kubectl apply -f install/kubernetes/helm/helm-service-account.yaml
    helm init --service-account tiller
    helm install install/kubernetes/helm/istio-init --name istio-init --namespace istio-system
    kubectl -n istio-system wait --for=condition=complete job --all
    helm install install/kubernetes/helm/istio --name istio --namespace istio-system  --values install/kubernetes/helm/istio/values-istio-sds-auth.yaml

    配置TLS安全网关

    • SDS (安全发现服务)趋于稳定、默认开启
    • 对等认证和请求认证配置分离
    • 自动 mTLS 从 alpha 变为 beta,默认开启
    • Node agent 和 Pilot agent 合并, 简化 Pod 安全策略的配置
    • 支持 first-party-jwt (ServiceAccountToken) 作为 third-party-jwt 的备用
    • …...

    安全发现服务(SDS):

    • 身份和证书管理
    • 实现安全配置自动化
    • 中心化 SDS Server
    • 优点:
      • 无需挂载 secret 卷
      • 动态更新证书,无需重启
      • 可监视多个证书密钥对
    对于 macOS 用户,请验证您是否使用通过 LibreSSL 库编译的 curl:
    接下来我们配置一个安全网关,为外部提供 HTTPS 的访问方式。首先,确认 curl 命令是否通过LibreSSL去编译的
    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

    如果上述命令输出的是如图所示的 LibreSSL 版本,则 curl 命令应按照此任务中的说明正确运行。否则,请尝试使用 curl 的其他实现,例如在 Linux 机器上。

    生成客户端和服务器证书和密钥

    创建用于服务签名的根证书和私钥:

    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

    部署 httpbin 服务

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: httpbin
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: httpbin
      labels:
        app: httpbin
        service: httpbin
    spec:
      ports:
      - name: http
        port: 8000
        targetPort: 80
      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:
          serviceAccountName: httpbin
          containers:
          - image: docker.io/kennethreitz/httpbin
            imagePullPolicy: IfNotPresent
            name: httpbin
            ports:
            - containerPort: 80

    配置单机TLS入口网关

    为 Ingress Gateway 创建 Secret:
    kubectl create -n istio-system secret tls httpbin-credential --key=httpbin.example.com.key --cert=httpbin.example.com.crt

    创建入口网关,并指定外部以 https 方式访问,并将 credentialName 的值指定为 httpbin-credential。这些值与 Secret 名称相同。 TLS 模式的值应为 SIMPLE

    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   # 简单模式,单向TLS
          credentialName: httpbin-credential # must be the same as secret
        hosts:
        - httpbin.example.com
    EOF

    配置网关的入口流量路由,定义相应的虚拟服务。

    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
    EOF

    如果 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 ]=-
    
           _...._
         .'  _ _ `.
        | ."` ^ `". _,
        \_;`"---"`|//
          |       ;/
          \_     _/
            `"""`
    

    curl 测试,请求验证是否生效:

    curl -HHost:httpbin.example.com 
    --resolve httpbin.example.com:443:${INGRESS_HOST} 
    --cacert example.com.crt "https://httpbin.example.com:443/status/418"

    配置选项

    双重保障:为应用设置不同级别的双向TLS 

    Istio 认证策略

    • 认证策略的分类
      • 对等认证(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}
    "
    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}
    "
    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}
    "
    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}
    "
    200
    [root@m1 ~]# 

    请求认证配置选项:

    授权策略配置选项:

    关于安全方面更多的内容可以参考官方文档的使用示例:

    参考:
    https://preliminary.istio.io/latest/zh/docs/tasks/traffic-management/ingress/secure-ingress/#configure-a-mutual-TLS-ingress-gateway
    https://cloud.tencent.com/developer/article/1765731
  • 相关阅读:
    <HTTP>ASI实现的注册方法:利用http的get和post两种方式
    <Ruby>社区服务端启动流程
    <iOS>ASIHTTPRequest和ASIDownloadCache实现本地缓存
    <iOS>关于Xcode上的Other linker flags
    <HTTP>ASI实现的登陆方法
    【pool drain】和【pool release】区别
    <UI>TableViewCELL长按事件
    <UI>UIView的autoresizingMask属性
    <UI>自定义UITableView的右侧索引
    <cocos2D>ccLabel相关
  • 原文地址:https://www.cnblogs.com/fat-girl-spring/p/15191067.html
Copyright © 2011-2022 走看看