安全问题

最终用户身份验证失败

使用 Istio,您可以通过 请求身份验证策略 为最终用户启用身份验证。按照以下步骤排查策略规范。

  1. 如果未设置 jwksUri,请确保 JWT 发行者采用 url 格式,并且可以在浏览器中打开 url + /.well-known/openid-configuration;例如,如果 JWT 发行者为 https://#,请确保 https://#/.well-known/openid-configuration 是一个有效的 url,并且可以在浏览器中打开。

    apiVersion: security.istio.io/v1
    kind: RequestAuthentication
    metadata:
      name: "example-3"
    spec:
      selector:
        matchLabels:
          app: httpbin
      jwtRules:
      - issuer: "testing@secure.istio.io"
        jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.24/security/tools/jwt/samples/jwks.json"
    
  2. 如果 JWT 令牌放置在 HTTP 请求的 Authorization 标头中,请确保 JWT 令牌有效(未过期等)。JWT 令牌中的字段可以通过使用在线 JWT 解析工具进行解码,例如 jwt.io

  3. 使用 istioctl proxy-config 命令验证目标工作负载的 Envoy 代理配置。

    应用上述示例策略后,使用以下命令检查入站端口 80 上的 listener 配置。您应该看到 envoy.filters.http.jwt_authn 过滤器,其设置与策略中指定的颁发者和 JWKS 相匹配。

    $ POD=$(kubectl get pod -l app=httpbin -n foo -o jsonpath={.items..metadata.name})
    $ istioctl proxy-config listener ${POD} -n foo --port 80 --type HTTP -o json
    <redacted>
                                {
                                    "name": "envoy.filters.http.jwt_authn",
                                    "typedConfig": {
                                        "@type": "type.googleapis.com/envoy.config.filter.http.jwt_authn.v2alpha.JwtAuthentication",
                                        "providers": {
                                            "origins-0": {
                                                "issuer": "testing@secure.istio.io",
                                                "localJwks": {
                                                    "inlineString": "*redacted*"
                                                },
                                                "payloadInMetadata": "testing@secure.istio.io"
                                            }
                                        },
                                        "rules": [
                                            {
                                                "match": {
                                                    "prefix": "/"
                                                },
                                                "requires": {
                                                    "requiresAny": {
                                                        "requirements": [
                                                            {
                                                                "providerName": "origins-0"
                                                            },
                                                            {
                                                                "allowMissing": {}
                                                            }
                                                        ]
                                                    }
                                                }
                                            }
                                        ]
                                    }
                                },
    <redacted>
    

授权过于严格或宽松

确保策略 YAML 文件中没有错别字

一个常见的错误是在 YAML 中无意中指定多个项目。以以下策略为例

apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: example
  namespace: foo
spec:
  action: ALLOW
  rules:
  - to:
    - operation:
        paths:
        - /foo
  - from:
    - source:
        namespaces:
        - foo

您可能希望策略允许请求,如果路径为 /foo 并且 源命名空间为 foo。但是,策略实际上允许请求,如果路径为 /foo 或者 源命名空间为 foo,这更加宽松。

在 YAML 语法中,from: 前面的 - 表示它是列表中的一个新元素。这在策略中创建了 2 个规则,而不是 1 个。在授权策略中,多个规则具有 OR 的语义。

要解决此问题,只需删除多余的 -,使策略只有一个规则,该规则允许请求,如果路径为 /foo 并且 源命名空间为 foo,这更加严格。

确保您没有在 TCP 端口上使用 HTTP 专用字段

授权策略将更加严格,因为 HTTP 专用字段(例如 hostpathheaders、JWT 等)在原始 TCP 连接中不存在。

ALLOW 策略的情况下,这些字段永远不会匹配。在 DENYCUSTOM 操作的情况下,这些字段被认为始终匹配。最终效果是更严格的策略,可能会导致意外拒绝。

检查 Kubernetes 服务定义,以验证端口是否 以正确协议命名。如果您在端口上使用 HTTP 专用字段,请确保端口名称带有 http- 前缀。

确保策略应用于正确的目标

检查工作负载选择器和命名空间,以确认它应用于正确的目标。您可以通过运行 istioctl x authz check POD-NAME.POD-NAMESPACE 来确定生效的授权策略。

注意策略中指定的动作

  • 如果没有指定,策略默认使用操作 ALLOW

  • 当工作负载同时应用多个操作(CUSTOMALLOWDENY)时,必须满足所有操作才能允许请求。换句话说,如果任何操作拒绝,则拒绝请求;只有在所有操作都允许的情况下才能允许请求。

  • AUDIT 操作不会执行访问控制,在任何情况下都不会拒绝请求。

阅读 授权隐式启用,了解评估顺序的更多详细信息。

确保 Istiod 接受策略

Istiod 将您的授权策略转换为代理并分发给代理。以下步骤将帮助您确保 Istiod 按预期工作

  1. 运行以下命令以启用 Istiod 中的调试日志记录

    $ istioctl admin log --level authorization:debug
    
  2. 使用以下命令获取 Istiod 日志

    $ kubectl logs $(kubectl -n istio-system get pods -l app=istiod -o jsonpath='{.items[0].metadata.name}') -c discovery -n istio-system
    
  3. 检查输出并验证是否存在错误。例如,您可能会看到类似以下内容

    2021-04-23T20:53:29.507314Z info ads Push debounce stable[31] 1: 100.981865ms since last change, 100.981653ms since last push, full=true
    2021-04-23T20:53:29.507641Z info ads XDS: Pushing:2021-04-23T20:53:29Z/23 Services:15 ConnectedEndpoints:2  Version:2021-04-23T20:53:29Z/23
    2021-04-23T20:53:29.507911Z debug authorization Processed authorization policy for httpbin-74fb669cc6-lpscm.foo with details:
        * found 0 CUSTOM actions
    2021-04-23T20:53:29.508077Z debug authorization Processed authorization policy for curl-557747455f-6dxbl.foo with details:
        * found 0 CUSTOM actions
    2021-04-23T20:53:29.508128Z debug authorization Processed authorization policy for httpbin-74fb669cc6-lpscm.foo with details:
        * found 1 DENY actions, 0 ALLOW actions, 0 AUDIT actions
        * generated config from rule ns[foo]-policy[deny-path-headers]-rule[0] on HTTP filter chain successfully
        * built 1 HTTP filters for DENY action
        * added 1 HTTP filters to filter chain 0
        * added 1 HTTP filters to filter chain 1
    2021-04-23T20:53:29.508158Z debug authorization Processed authorization policy for curl-557747455f-6dxbl.foo with details:
        * found 0 DENY actions, 0 ALLOW actions, 0 AUDIT actions
    2021-04-23T20:53:29.509097Z debug authorization Processed authorization policy for curl-557747455f-6dxbl.foo with details:
        * found 0 CUSTOM actions
    2021-04-23T20:53:29.509167Z debug authorization Processed authorization policy for curl-557747455f-6dxbl.foo with details:
        * found 0 DENY actions, 0 ALLOW actions, 0 AUDIT actions
    2021-04-23T20:53:29.509501Z debug authorization Processed authorization policy for httpbin-74fb669cc6-lpscm.foo with details:
        * found 0 CUSTOM actions
    2021-04-23T20:53:29.509652Z debug authorization Processed authorization policy for httpbin-74fb669cc6-lpscm.foo with details:
        * found 1 DENY actions, 0 ALLOW actions, 0 AUDIT actions
        * generated config from rule ns[foo]-policy[deny-path-headers]-rule[0] on HTTP filter chain successfully
        * built 1 HTTP filters for DENY action
        * added 1 HTTP filters to filter chain 0
        * added 1 HTTP filters to filter chain 1
        * generated config from rule ns[foo]-policy[deny-path-headers]-rule[0] on TCP filter chain successfully
        * built 1 TCP filters for DENY action
        * added 1 TCP filters to filter chain 2
        * added 1 TCP filters to filter chain 3
        * added 1 TCP filters to filter chain 4
    2021-04-23T20:53:29.510903Z info ads LDS: PUSH for node:curl-557747455f-6dxbl.foo resources:18 size:85.0kB
    2021-04-23T20:53:29.511487Z info ads LDS: PUSH for node:httpbin-74fb669cc6-lpscm.foo resources:18 size:86.4kB
    

    这表明 Istiod 生成了

    • 具有策略 ns[foo]-policy[deny-path-headers]-rule[0] 的 HTTP 过滤器配置,用于工作负载 httpbin-74fb669cc6-lpscm.foo

    • 具有策略 ns[foo]-policy[deny-path-headers]-rule[0] 的 TCP 过滤器配置,用于工作负载 httpbin-74fb669cc6-lpscm.foo

确保 Istiod 正确地将策略分发到代理

Istiod 将授权策略分发给代理。以下步骤将帮助您确保 Istiod 按预期工作

  1. 运行以下命令以获取 httpbin 工作负载的代理配置转储

    $ kubectl exec  $(kubectl get pods -l app=httpbin -o jsonpath='{.items[0].metadata.name}') -c istio-proxy -- pilot-agent request GET config_dump
    
  2. 检查日志并验证

    • 日志包含一个 envoy.filters.http.rbac 过滤器,用于在每个传入请求上强制执行授权策略。
    • 更新授权策略后,Istio 会相应地更新过滤器。
  3. 以下输出表示 httpbin 的代理已启用 envoy.filters.http.rbac 过滤器,该过滤器具有拒绝任何人访问路径 /headers 的规则。

    {
     "name": "envoy.filters.http.rbac",
     "typed_config": {
      "@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC",
      "rules": {
       "action": "DENY",
       "policies": {
        "ns[foo]-policy[deny-path-headers]-rule[0]": {
         "permissions": [
          {
           "and_rules": {
            "rules": [
             {
              "or_rules": {
               "rules": [
                {
                 "url_path": {
                  "path": {
                   "exact": "/headers"
                  }
                 }
                }
               ]
              }
             }
            ]
           }
          }
         ],
         "principals": [
          {
           "and_ids": {
            "ids": [
             {
              "any": true
             }
            ]
           }
          }
         ]
        }
       }
      },
      "shadow_rules_stat_prefix": "istio_dry_run_allow_"
     }
    },
    

确保代理正确地执行策略

代理最终会强制执行授权策略。以下步骤将帮助您确保代理按预期工作

  1. 使用以下命令打开代理中的授权调试日志记录

    $ istioctl proxy-config log deploy/httpbin --level "rbac:debug"
    
  2. 验证您是否看到以下输出

    active loggers:
      ... ...
      rbac: debug
      ... ...
    
  3. httpbin 工作负载发送一些请求以生成一些日志。

  4. 使用以下命令打印代理日志

    $ kubectl logs $(kubectl get pods -l app=httpbin -o jsonpath='{.items[0].metadata.name}') -c istio-proxy
    
  5. 检查输出并验证

    • 输出日志显示 enforced allowedenforced denied,具体取决于请求是否被允许或被拒绝。

    • 您的授权策略期望从请求中提取的数据。

  6. 以下是路径 /httpbin 的请求的示例输出

    ...
    2021-04-23T20:43:18.552857Z debug envoy rbac checking request: requestedServerName: outbound_.8000_._.httpbin.foo.svc.cluster.local, sourceIP: 10.44.3.13:46180, directRemoteIP: 10.44.3.13:46180, remoteIP: 10.44.3.13:46180,localAddress: 10.44.1.18:80, ssl: uriSanPeerCertificate: spiffe://cluster.local/ns/foo/sa/curl, dnsSanPeerCertificate: , subjectPeerCertificate: , headers: ':authority', 'httpbin:8000'
    ':path', '/headers'
    ':method', 'GET'
    ':scheme', 'http'
    'user-agent', 'curl/7.76.1-DEV'
    'accept', '*/*'
    'x-forwarded-proto', 'http'
    'x-request-id', '672c9166-738c-4865-b541-128259cc65e5'
    'x-envoy-attempt-count', '1'
    'x-b3-traceid', '8a124905edf4291a21df326729b264e9'
    'x-b3-spanid', '21df326729b264e9'
    'x-b3-sampled', '0'
    'x-forwarded-client-cert', 'By=spiffe://cluster.local/ns/foo/sa/httpbin;Hash=d64cd6750a3af8685defbbe4dd8c467ebe80f6be4bfe9ca718e81cd94129fc1d;Subject="";URI=spiffe://cluster.local/ns/foo/sa/curl'
    , dynamicMetadata: filter_metadata {
      key: "istio_authn"
      value {
        fields {
          key: "request.auth.principal"
          value {
            string_value: "cluster.local/ns/foo/sa/curl"
          }
        }
        fields {
          key: "source.namespace"
          value {
            string_value: "foo"
          }
        }
        fields {
          key: "source.principal"
          value {
            string_value: "cluster.local/ns/foo/sa/curl"
          }
        }
        fields {
          key: "source.user"
          value {
            string_value: "cluster.local/ns/foo/sa/curl"
          }
        }
      }
    }
    
    2021-04-23T20:43:18.552910Z debug envoy rbac enforced denied, matched policy ns[foo]-policy[deny-path-headers]-rule[0]
    ...
    

    日志 enforced denied, matched policy ns[foo]-policy[deny-path-headers]-rule[0] 表示请求被策略 ns[foo]-policy[deny-path-headers]-rule[0] 拒绝。

  7. 以下是 干运行模式 中的授权策略的示例输出

    ...
    2021-04-23T20:59:11.838468Z debug envoy rbac checking request: requestedServerName: outbound_.8000_._.httpbin.foo.svc.cluster.local, sourceIP: 10.44.3.13:49826, directRemoteIP: 10.44.3.13:49826, remoteIP: 10.44.3.13:49826,localAddress: 10.44.1.18:80, ssl: uriSanPeerCertificate: spiffe://cluster.local/ns/foo/sa/curl, dnsSanPeerCertificate: , subjectPeerCertificate: , headers: ':authority', 'httpbin:8000'
    ':path', '/headers'
    ':method', 'GET'
    ':scheme', 'http'
    'user-agent', 'curl/7.76.1-DEV'
    'accept', '*/*'
    'x-forwarded-proto', 'http'
    'x-request-id', 'e7b2fdb0-d2ea-4782-987c-7845939e6313'
    'x-envoy-attempt-count', '1'
    'x-b3-traceid', '696607fc4382b50017c1f7017054c751'
    'x-b3-spanid', '17c1f7017054c751'
    'x-b3-sampled', '0'
    'x-forwarded-client-cert', 'By=spiffe://cluster.local/ns/foo/sa/httpbin;Hash=d64cd6750a3af8685defbbe4dd8c467ebe80f6be4bfe9ca718e81cd94129fc1d;Subject="";URI=spiffe://cluster.local/ns/foo/sa/curl'
    , dynamicMetadata: filter_metadata {
      key: "istio_authn"
      value {
        fields {
          key: "request.auth.principal"
          value {
            string_value: "cluster.local/ns/foo/sa/curl"
          }
        }
        fields {
          key: "source.namespace"
          value {
            string_value: "foo"
          }
        }
        fields {
          key: "source.principal"
          value {
            string_value: "cluster.local/ns/foo/sa/curl"
          }
        }
        fields {
          key: "source.user"
          value {
            string_value: "cluster.local/ns/foo/sa/curl"
          }
        }
      }
    }
    
    2021-04-23T20:59:11.838529Z debug envoy rbac shadow denied, matched policy ns[foo]-policy[deny-path-headers]-rule[0]
    2021-04-23T20:59:11.838538Z debug envoy rbac no engine, allowed by default
    ...
    

    日志 shadow denied, matched policy ns[foo]-policy[deny-path-headers]-rule[0] 表示请求将被干运行策略 ns[foo]-policy[deny-path-headers]-rule[0] 拒绝。

    日志 no engine, allowed by default 表示请求实际上被允许,因为干运行策略是工作负载上唯一的策略。

密钥和证书错误

如果您怀疑 Istio 使用的一些密钥或证书不正确,您可以从任何 pod 检查其内容

$ istioctl proxy-config secret curl-8f795f47d-4s4t7
RESOURCE NAME     TYPE           STATUS     VALID CERT     SERIAL NUMBER                               NOT AFTER                NOT BEFORE
default           Cert Chain     ACTIVE     true           138092480869518152837211547060273851586     2020-11-11T16:39:48Z     2020-11-10T16:39:48Z
ROOTCA            CA             ACTIVE     true           288553090258624301170355571152070165215     2030-11-08T16:34:52Z     2020-11-10T16:34:52Z

通过传递 -o json 标志,您可以将完整证书内容传递给 openssl 以分析其内容

$ istioctl proxy-config secret curl-8f795f47d-4s4t7 -o json | jq '[.dynamicActiveSecrets[] | select(.name == "default")][0].secret.tlsCertificate.certificateChain.inlineBytes' -r | base64 -d | openssl x509 -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            99:59:6b:a2:5a:f4:20:f4:03:d7:f0:bc:59:f5:d8:40
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: O = k8s.cluster.local
        Validity
            Not Before: Jun  4 20:38:20 2018 GMT
            Not After : Sep  2 20:38:20 2018 GMT
...
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Alternative Name:
                URI:spiffe://cluster.local/ns/my-ns/sa/my-sa
...

确保显示的证书包含有效信息。特别是,Subject Alternative Name 字段应该是 URI:spiffe://cluster.local/ns/my-ns/sa/my-sa

双向 TLS 错误

如果您怀疑双向 TLS 存在问题,首先确保 Istiod 正常运行,其次确保 密钥和证书被正确传递 给 sidecar。

如果到目前为止一切正常,下一步是验证是否应用了正确的 身份验证策略 以及是否到位正确的目标规则。

如果您怀疑客户端 sidecar 可能错误地发送双向 TLS 或明文流量,请检查 Grafana 工作负载仪表板。出站请求将标注是否使用了 mTLS。检查完此内容后,如果您认为客户端 sidecar 存在故障,请在 GitHub 上报告问题。

这些信息对您有用吗?
您有什么改进建议吗?

感谢您的反馈!