服务网格Istio自身服务的安全风险

Istio是目前最受关注的服务网格之一,越来越多的公司开始落地Service Mesh架构,并将已有的系统接入Service Mesh。同时随着零信任网络的发展,东西向,南北向流量都需要提供足够的安全保护,因为Istio自身提供了多种安全特性,所以也出现不少基于Istio的零信任架构。在安全中通常关注的重点是南北流量,而我们应该认识到,在云原生的环境下,东西流量安全也应该受到足够的重视。

在Istio默认的配置中,其在集群内暴露了许多未授权和明文传输的服务。通常情况下,这会引起攻击者的注意,当恶意攻击者进入被控制的容器或发现SSRF漏洞后,往往可以利用这些自身的服务扩大攻击范围。

Istio 架构

Istio是一个完全开源的服务网格,它可以作为透明的一层接入到现有的分布式应用程序里。它也是一个平台,拥有可以集成任何日志、遥测和策略系统的API接口。Istio多样化的特性可以帮助我们成功且高效地运行分布式微服务架构,并提供保护、连接和监控微服务的统一方法。同时Istio的安全特性解放了开发人员,使其只需要专注于应用程序级别的安全。Istio提供了底层的安全通信通道,并为大规模的服务通信管理认证、授权和加密。

在1.5版本时Istio进行了一次大的变更,由原来的微服务架构转变成了单体架构。在之前版本其由Pilot、Citadel、Galley等多个微服务组成,现在只需要一个istiod的单体服务。

数据面(Data Plane)

数据面负责数据的转发,一般我们常见的通用网关,Web Server,比如Nginx、Traefik都可以认为是数据面的一种。在Service Mesh的开源世界中,Envoy可以说是最知名的数据面了。Istio使用了Envoy来处理集群中服务之间以及从服务到外部服务的进出流量。它简化并增强了微服务之间通过基础平台提供的网络交互。

另外数据面并非局限于网关类产品,实际上某些RPC框架也可以充当数据面,比如gRPC就已经支持完整的xDS,其也可以当作数据面使用。需要知道的是,一般我们会把负责数据转发的数据面也称为Sidecar。

控制面(Control Plane)

通过xDS协议对数据面进行配置下发,以控制数据面的行为,比如路由转发、负载均衡、服务治理等配置下发。

最新版本的Istio控制面由以下几个组件组成。

  • Pilot:Istio控制面中最核心的模块,负责运行时配置下发,具体来说,就是和Envoy之间基于xDS协议进行的各种Envoy配置信息的推送,包括服务发现、路由发现、集群发现、监听器发现等。
  • Citadel:负责证书的分发和轮换,使Sidecar代理两端实现双向TLS认证、访问授权等。
  • Galley:配置信息的格式和正确性校验,将配置信息提供给Pilot使用。

数据面风险

istio数据平面的核心是proxy,其以Sidecar模式运行,每个服务pod启动时伴随启动istio-init和proxy容器。其中proxy容器对内外开放了许多端口用于监控以及运维。

# netstat -ln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       
tcp        0      0 0.0.0.0:15021           0.0.0.0:*               LISTEN      
tcp        0      0 0.0.0.0:15090           0.0.0.0:*               LISTEN      
tcp        0      0 127.0.0.1:15000         0.0.0.0:*               LISTEN      
tcp        0      0 0.0.0.0:15001           0.0.0.0:*               LISTEN      
tcp        0      0 0.0.0.0:15006           0.0.0.0:*               LISTEN      
tcp        0      0 :::15020                :::*                    LISTEN
 

其会使用的全部服务端口信息如下:

Telemetry & Health 服务

Telemetry服务端口为15090,用于对外提供Envoy的性能统计指标。

# curl http://127.0.0.1:15090/stats/prometheus
# TYPE envoy_cluster_assignment_stale counter
envoy_cluster_assignment_stale{cluster_name="xds-grpc"} 0
 
# TYPE envoy_cluster_assignment_timeout_received counter
envoy_cluster_assignment_timeout_received{cluster_name="xds-grpc"} 0
 
# TYPE envoy_cluster_bind_errors counter
envoy_cluster_bind_errors{cluster_name="xds-grpc"} 0
 
# TYPE envoy_cluster_default_total_match_count counter
envoy_cluster_default_total_match_count{cluster_name="xds-grpc"} 1
 
# TYPE envoy_cluster_http2_dropped_headers_with_underscores counter
envoy_cluster_http2_dropped_headers_with_underscores{cluster_name="xds-grpc"} 0
 
# TYPE envoy_cluster_http2_header_overflow counter
envoy_cluster_http2_header_overflow{cluster_name="xds-grpc"} 0
......
 

Health Check服务如下:

# curl http://127.0.0.1:15021/healthz/ready -v
*   Trying 127.0.0.1:15021...
* Connected to 127.0.0.1 (127.0.0.1) port 15021 (#0)
> GET /healthz/ready HTTP/1.1
> Host: 127.0.0.1:15021
> User-Agent: curl/7.69.1
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< date: Tue, 16 Mar 2021 05:31:43 GMT
< content-length: 0
< x-envoy-upstream-service-time: 0
< server: envoy
< 
* Connection #0 to host 127.0.0.1 left intact
 

Debug 服务

15000端口提供了Envoy admin API,该端口绑定在本地环回地址上,只能在Pod内访问。当攻击者控制了某容器,即可请求该服务来获取敏感信息。

# curl http://127.0.0.1:15000/help
admin commands are:
  /: Admin home page
  /certs: print certs on machine
  /clusters: upstream cluster status
  /config_dump: dump current Envoy configs (experimental)
  /contention: dump current Envoy mutex contention stats (if enabled)
  /cpuprofiler: enable/disable the CPU profiler
  /drain_listeners: drain listeners
  /healthcheck/fail: cause the server to fail health checks
  /healthcheck/ok: cause the server to pass health checks
  /heapprofiler: enable/disable the heap profiler
  /help: print out list of admin commands
  /hot_restart_version: print the hot restart compatibility version
  /init_dump: dump current Envoy init manager information (experimental)
  /listeners: print listener info
  /logging: query/change logging levels
  /memory: print current allocation/heap usage
  /quitquitquit: exit the server
  /ready: print server state, return 200 if LIVE, otherwise return 503
  /reopen_logs: reopen access logs
  /reset_counters: reset all counters to zero
  /runtime: print runtime values
  /runtime_modify: modify runtime values
  /server_info: print server version/status information
  /stats: print server stats
  /stats/prometheus: print server stats in prometheus format
  /stats/recentlookups: Show recent stat-name lookups
  /stats/recentlookups/clear: clear list of stat-name lookups and counter
  /stats/recentlookups/disable: disable recording of reset stat-name lookup names
  /stats/recentlookups/enable: enable recording of reset stat-name lookup names
  
# curl http://127.0.0.1:15000/config_dump
{
 "configs": [
  {
   "@type": "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump",
   "bootstrap": {
    "node": {
     "id": "sidecar~172.17.0.3~sleep-5d4c8b88ff-wgzkh.foo~foo.svc.cluster.local",
     "cluster": "sleep.foo",
     "metadata": {
      "NAMESPACE": "foo",
      "EXCHANGE_KEYS": "NAME,NAMESPACE,INSTANCE_IPS,LABELS,OWNER,PLATFORM_METADATA,WORKLOAD_NAME,MESH_ID,SERVICE_ACCOUNT,CLUSTER_ID",
      "INSTANCE_IPS": "172.17.0.3",
      "POD_PORTS": "[\n]",
      "INTERCEPTION_MODE": "REDIRECT",
      "MESH_ID": "cluster.local",
      "SERVICE_ACCOUNT": "sleep",
      "OWNER": "kubernetes://apis/apps/v1/namespaces/default/deployments/sleep",
      "WORKLOAD_NAME": "sleep",
      "ISTIO_VERSION": "1.8.2",
      "PROXY_CONFIG": {
       "tracing": {
        "zipkin": {
         "address": "zipkin.istio-system:9411"
        }
       },
       "statusPort": 15020,
       "serviceCluster": "sleep.foo",
       "envoyMetricsService": {},
       "binaryPath": "/usr/local/bin/envoy",
       "discoveryAddress": "istiod.istio-system.svc:15012",
       "concurrency": 2,
       "envoyAccessLogService": {},
       "statNameLength": 189,
       "configPath": "./etc/istio/proxy",
       "parentShutdownDuration": "60s",
       "proxyAdminPort": 15000,
       "controlPlaneAuthPolicy": "MUTUAL_TLS",
       "drainDuration": "45s",
       "proxyMetadata": {
        "DNS_AGENT": ""
       },
       "terminationDrainDuration": "5s"
      },
            ......
      
 }
 

控制面风险

其会使用的全部服务端口信息如下:

XDS 服务

xDS协议是Envoy获取配置信息的传输协议,也是Istio与Envoy连接的桥梁。Istio控制面的XDS服务提供了各种类型的服务发现能力。需要了解的是,在Istio中XDS和CA服务一起通过端口15010和15012暴露,其中15010提供明文传输,而15012提供了TLS支持。由于端口15010对外暴露了非TLS的XDS服务,那么在集群内意味着我们就可以通过请求控制面的该明文的服务来获取敏感的信息。

# ./xds -u istiod.istio-system:15010 -v 3
Response CDS 23
{"name":"BlackHoleCluster","type":"STATIC","connectTimeout":"10s"}
{"name":"InboundPassthroughClusterIpv4","type":"ORIGINAL_DST","connectTimeout":"10s","lbPolicy":"CLUSTER_PROVIDED","circuitBreakers":{"thresholds":[{"maxConnections":4294967295,"maxPendingRequests":4294967295,"maxRequests":4294967295,"maxRetries":4294967295}]},"upstreamBindConfig":{"sourceAddress":{"address":"127.0.0.6","portValue":0}},"protocolSelection":"USE_DOWNSTREAM_PROTOCOL"}
{"name":"PassthroughCluster","type":"ORIGINAL_DST","connectTimeout":"10s","lbPolicy":"CLUSTER_PROVIDED","circuitBreakers":{"thresholds":[{"maxConnections":4294967295,"maxPendingRequests":4294967295,"maxRequests":4294967295,"maxRetries":4294967295}]},"protocolSelection":"USE_DOWNSTREAM_PROTOCOL"}
{"name":"inbound|80||","type":"STATIC","connectTimeout":"10s","loadAssignment":{"clusterName":"inbound|80||","endpoints":[{"lbEndpoints":[{"endpoint":{"address":{"socketAddress":{"address":"127.0.0.1","portValue":80}}}}]}]},"circuitBreakers":{"thresholds":[{"maxConnections":4294967295,"maxPendingRequests":4294967295,"maxRequests":4294967295,"maxRetries":4294967295}]},"metadata":{"filterMetadata":{"istio":{"services":[{"host":"sleep.foo.svc.cluster.local","name":"sleep","namespace":"foo"}]}}}}
{"transportSocketMatches":[{"name":"tlsMode-istio","match":{"tlsMode":"istio"},"transportSocket":{"name":"envoy.transport_sockets.tls","typedConfig":{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext","commonTlsContext":{"tlsCertificateSdsSecretConfigs":[{"name":"default","sdsConfig":{"apiConfigSource":{"apiType":"GRPC","transportApiVersion":"V3","grpcServices":[{"envoyGrpc":{"clusterName":"sds-grpc"}}]},"initialFetchTimeout":"0s","resourceApiVersion":"V3"}}],"combinedValidationContext":{"defaultValidationContext":{},"validationContextSdsSecretConfig":{"name":"ROOTCA","sdsConfig":{"apiConfigSource":{"apiType":"GRPC","transportApiVersion":"V3","grpcServices":[{"envoyGrpc":{"clusterName":"sds-grpc"}}]},"initialFetchTimeout":"0s","resourceApiVersion":"V3"}}},"alpnProtocols":["istio-peer-exchange","istio","h2"]},"sni":"outbound_.15010_._.istiod.istio-system.svc.cluster.local"}}},{"name":"tlsMode-disabled","match":{},"transportSocket":{"name":"envoy.transport_sockets.raw_buffer"}}],"name":"outbound|15010||istiod.istio-system.svc.cluster.local","type":"EDS","edsClusterConfig":{"edsConfig":{"ads":{},"resourceApiVersion":"V3"},"serviceName":"outbound|15010||istiod.istio-system.svc.cluster.local"},"connectTimeout":"10s","circuitBreakers":{"thresholds":[{"maxConnections":4294967295,"maxPendingRequests":4294967295,"maxRequests":4294967295,"maxRetries":4294967295}]},"http2ProtocolOptions":{"maxConcurrentStreams":1073741824},"metadata":{"filterMetadata":{"istio":{"default_original_port":15010,"services":[{"host":"istiod.istio-system.svc.cluster.local","name":"istiod","namespace":"istio-system"}]}}}}
......
 

对此,我们可以在istiod配置中增加 --grpcAddr="" 来禁用该端口。

Debug 服务

在istiod即Istio的控制面中同时也提供了Debug服务,这是我们在这些服务中应该注意的重点,它在默认情况下开启,其中端口15014(Control plane monitoring)和端口8080(Debug interface)提供了该服务。通过该服务我们可以获取到集群内的各种信息。

比如在我们在配置某些服务的AuthorizationPolicy,为其提供某种授权验证方式后。

我们可以通过Pilot debug API轻松获取到其配置信息,从而利用这些信息通过授权验证。这是东西向流量中很大的安全隐患,如果使用了Istio,那么请最好排查该功能的存在可能造成的影响。

# curl http://istiod.istio-system:15014/debug/configz
 
......
{
      "kind": "AuthorizationPolicy",
      "apiVersion": "security.istio.io/v1beta1",
      "metadata": {
        "name": "httpbin",
        "namespace": "foo",
        "resourceVersion": "3693",
        "creationTimestamp": "2021-02-20T11:52:33Z",
        "annotations": {
          "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"security.istio.io/v1beta1\",\"kind\":\"AuthorizationPolicy\",\"metadata\":{\"annotations\":{},\"name\":\"httpbin\",\"namespace\":\"foo\"},\"spec\":{\"rules\":[{\"from\":[{\"source\":{\"requestPrincipals\":[\"*\"]}}]},{\"to\":[{\"operation\":{\"notPaths\":[\"/ip\"]}}]}],\"selector\":{\"matchLabels\":{\"app\":\"httpbin\"}}}}\n"
        }
      },
      "spec": {
        "rules": [
          {
            "from": [
              {
                "source": {
                  "requestPrincipals": [
                    "*"
                  ]
                }
              }
            ]
          },
          {
            "to": [
              {
                "operation": {
                  "notPaths": [
                    "/ip"
                  ]
                }
              }
            ]
          }
        ],
        "selector": {
          "matchLabels": {
            "app": "httpbin"
          }
        }
      }
    },
    {
      "kind": "RequestAuthentication",
      "apiVersion": "security.istio.io/v1beta1",
      "metadata": {
        "name": "httpbin",
        "namespace": "foo",
        "resourceVersion": "3692",
        "creationTimestamp": "2021-02-20T11:52:33Z",
        "annotations": {
          "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"security.istio.io/v1beta1\",\"kind\":\"RequestAuthentication\",\"metadata\":{\"annotations\":{},\"name\":\"httpbin\",\"namespace\":\"foo\"},\"spec\":{\"jwtRules\":[{\"issuer\":\"issuer-foo\",\"jwksUri\":\"https://example.com/.well-known/jwks.json\"}],\"selector\":{\"matchLabels\":{\"app\":\"httpbin\"}}}}\n"
        }
      },
      "spec": {
        "jwtRules": [
          {
            "issuer": "issuer-foo",
            "jwksUri": "https://example.com/.well-known/jwks.json"
          }
        ],
        "selector": {
          "matchLabels": {
            "app": "httpbin"
          }
        }
      }
    }
......
 

对此,我们可以在istiod中设置环境变量 ENABLE_DEBUG_ON_HTTP=false 来关闭该服务。不过需要注意的是,istioctl中的许多命令都依赖于这个接口,这意味着它会普遍存在,我们也更应该关注它的安全风险。

总结

在云原生的环境下,东西流量安全应该得到足够的重视,而随着越来越多自动化运维工具的介入,也会对信息监控、采集的安全性,运维与生产网络之间的安全隔离提出更高的要求。

本文简要描述了在Istio的默认配置中,其在集群内暴露的许多未授权和明文传输的服务。在我与Istio官方反馈此类安全问题后,官方后续更新并完善了Istio的文档,在最佳实践中补充了这些情况和应对方式,本文也是对其内容的一些补充说明和思考。

原文链接: http://rui0.cn/archives/1597

如果觉得本文对你有帮助,可以关注一下我公众号,回复关键字【面试】即可得到一份Java核心知识点整理与一份面试大礼包!另有更多技术干货文章以及相关资料共享,大家一起学习进步!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值