API Server 安全风险(一):不安全的服务(理论篇)

admin 2024年12月5日22:19:26评论9 views字数 10041阅读33分28秒阅读模式

unsetunset0x00 前言unsetunset

在实际的工作和攻防对抗的时候,您是否遇到过以下问题:

  1. 由于 API Server 错误配置,导致攻击者可以接管集群。
  2. 根据教程对 API Server 不安全端口的风险进行漏洞复现,却无法复现成功。
  3. 无法深入浅出的分析 API Server 不安全的服务的风险,被 API Server 各种大小版本之间的变化弄得一团乱。
  4. 由于环境限制,无法搭建多个 Kubernetes 环境进行靶场练习。
  5. 初学云原生安全时,通常感到无从下手,难度较大。

为了解决上述问题,喵苗安全专家组成员特意制作了一系列《Kubernetes 安全防护指南(基础篇)》免费版图文教程,帮助大家入门云原生安全。本次课程的亮点有:

  1. 根据 Kubernetes 各个版本发布的时间线,带领大家阅读 Kubernetes 关键源码,深入浅出地分析 Kubernetes 风险。
  2. 制作了开源的 Kubernetes 练习靶场,可在本地一键启动,无需虚拟机和服务器。
  3. 精心绘制的插图、诙谐有趣的叙述方式、理论知识与靶场练习相结合,带领你领略 Kubernetes 的安全。
  4. 搭建一个系统的 Kubernetes 学习框架,防止“管中窥豹,可见一斑”。
  5. 结合自身经验,帮助您总结关键知识和技能。

unsetunset0x01 基本概念unsetunset

API Server 就像它名字所描述的那样,主要负责提供 REST API 接口 ,方便我们对 Kubernetes 集群进行管控。拥有 API Server 全部权限的用户相当于拥有了集群中所有机器的 root 访问权限。

而命令行工具 kubectl 就是 API Server 的客户端工具,主要负责向 API Server 发送请求以实现资源和工作负载的管理。需要注意的是,任何对 API Server 有写权限的人都可以以相同的方式对集群进行管控。

API Server 安全风险(一):不安全的服务(理论篇)
图1:API Server 与 kubectl 的关系(简化版)

如图所示,当我们使用 kubectl 获取集群信息的时候,只要通过-v 8 将日志的等级调到 8,就可以看到完整的 HTTP(S) 数据包。

API Server 安全风险(一):不安全的服务(理论篇)
图2:kubectl 与 API Server 的交互本质上只是发起 HTTP(S)请求

unsetunset0x02 本地访问不安全的服务unsetunset

早期,Kubernetes 集群需要允许负载均衡等组件进行健康检查和发现,所以默认情况下,API Server 会在本地监听不安全的服务。

以 2018 年 3 月 7 日发布的 Kubernetes v1.9.0 为例,Kubernetes 在创建 API Server 的时候,会创建不安全的服务InsecureServing

// NewServerRunOptions creates a new ServerRunOptions object with default parameters// 代码片段链接:https://github.com/kubernetes/kubernetes/blob/b5ec061c7ab9995a801206ea74f614ced04206d2/cmd/kube-apiserver/app/options/options.go#L76funcNewServerRunOptions() *ServerRunOptions {  s := ServerRunOptions{    GenericServerRunOptions: genericoptions.NewServerRunOptions(),    Etcd:                 genericoptions.NewEtcdOptions(storagebackend.NewDefaultConfig(kubeoptions.DefaultEtcdPathPrefix, nil)),    SecureServing:        kubeoptions.NewSecureServingOptions(),// API Server 会创建不安全的服务    InsecureServing:      kubeoptions.NewInsecureServingOptions(),    Audit:                genericoptions.NewAuditOptions(),    Features:             genericoptions.NewFeatureOptions(),    Admission:            genericoptions.NewAdmissionOptions(),    Authentication:       kubeoptions.NewBuiltInAuthenticationOptions().WithAll(),    Authorization:        kubeoptions.NewBuiltInAuthorizationOptions(),    CloudProvider:        kubeoptions.NewCloudProviderOptions(),    StorageSerialization: kubeoptions.NewStorageSerializationOptions(),    APIEnablement:        kubeoptions.NewAPIEnablementOptions(),    EnableLogsHandler:      true,    EventTTL:               1 * time.Hour,    MasterCount:            1,    EndpointReconcilerType: string(reconcilers.MasterCountReconcilerType),    KubeletConfig: kubeletclient.KubeletClientConfig{      Port:         ports.KubeletPort,      ReadOnlyPort: ports.KubeletReadOnlyPort,      PreferredAddressTypes: []string{// --override-hostnamestring(api.NodeHostName),// internal, preferring DNS if reportedstring(api.NodeInternalDNS),string(api.NodeInternalIP),// external, preferring DNS if reportedstring(api.NodeExternalDNS),string(api.NodeExternalIP),      },      EnableHttps: true,      HTTPTimeout: time.Duration(5) * time.Second,    },    ServiceNodePortRange: kubeoptions.DefaultServiceNodePortRange,  }// Overwrite the default for storage data format.  s.Etcd.DefaultStorageMediaType = "application/vnd.kubernetes.protobuf"// register all admission plugins  RegisterAllAdmissionPlugins(s.Admission.Plugins)// Set the default for admission plugins names  s.Admission.PluginNames = []string{"AlwaysAdmit"}return &s}

而这个不安全的服务的端口就是8080,只能在本地进行访问,任何对该端口的请求都会跳过身份验证和权限检查。

// NewInsecureServingOptions is for creating an unauthenticated, unauthorized, insecure port.// No one should be using these anymore.// 代码片段链接:https://github.com/kubernetes/kubernetes/blob/b5ec061c7ab9995a801206ea74f614ced04206d2/pkg/kubeapiserver/options/serving.go#L75C1-L83C1funcNewInsecureServingOptions() *InsecureServingOptions {return &InsecureServingOptions{    BindAddress: net.ParseIP("127.0.0.1"),// 不安全服务的端口为 8080    BindPort:    8080,  }}

我们可以直接通过curl 命令进行检查,看看本地是否在监听不安全的服务。若命令执行过后,列出了 API 接口,那么不安全的服务是打开的状态。

curl localhost:8080
API Server 安全风险(一):不安全的服务(理论篇)
图3:本地在监听不安全的服务

若该服务能被任何人进行访问,那么任何能够访问 master 节点的人都可以对集群进行管控,这是极其不安全的。所以默认情况下,这个不安全的服务只能在本地进行访问。

API Server 安全风险(一):不安全的服务(理论篇)

图 4:默认情况下,不安全的服务只能通过本地进行访问

unsetunset0x03 远程访问不安全的服务unsetunset

当然,这个不安全的服务也可以通过配置进行修改,让它可以被远程访问到。具体需要修改的 Flag 就是--insecure-bind-address--insecure-port

--insecure-port 用于提供未加密、未认证访问的服务端口。之所以会有这个设计,是因为 Kubernetes 假设了用户已经配置了防火墙规则,这个端口无法从集群外部进行访问,并且 nginx 默认会将集群公共地址上的 443 端口代理到这个端口。

--insecure-bind-address 主要是与--insecure-port 一起使用,若设置为0.0.0.0,则允许所有的 IPv4 接口进行访问;若设置为::,则允许所有的 IPv6 接口进行访问。

如图所示,我们可以将--insecure-bind-address 设置为0.0.0.0--insecure-port 设置为8080 ,以达到可远程访问不安全的服务的目的。

API Server 安全风险(一):不安全的服务(理论篇)
图5: /etc/kubernetes/manifests/kube-apiserver.yaml 配置文件

配置生效过后,只要我们获取到集群的 IP 地址,就能够对集群的不安全服务进行远程访问与控制。

由此看来,--insecure-bind-address--insecure-port 是非常不安全的。

API Server 安全风险(一):不安全的服务(理论篇)
图6:对集群的不安全服务进行远程访问

所以,在 2018 年 3 月 27 日发布的 v1.10.0 版本中,Kubernetes 将--insecure-bind-address--insecure-port 这两个 Flag 标记为弃用的状态,并宣称在未来的版本中将完全移除。

// 代码片段链接:https://github.com/kubernetes/kubernetes/blob/f170ef66340f6355d331ed90902574ff0532a20a/pkg/kubeapiserver/options/serving.go#L98func(s *InsecureServingOptions)AddFlags(fs *pflag.FlagSet) {  fs.IPVar(&s.BindAddress, "insecure-bind-address", s.BindAddress, ""+"The IP address on which to serve the --insecure-port (set to 0.0.0.0 for all IPv4 interfaces and :: for all IPv6 interfaces).")  fs.MarkDeprecated("insecure-bind-address", "This flag will be removed in a future version.")  fs.Lookup("insecure-bind-address").Hidden = false  fs.IntVar(&s.BindPort, "insecure-port", s.BindPort, ""+"The port on which to serve unsecured, unauthenticated access. It is assumed "+"that firewall rules are set up such that this port is not reachable from outside of "+"the cluster and that port 443 on the cluster's public address is proxied to this "+"port. This is performed by nginx in the default setup. Set to zero to disable.")  fs.MarkDeprecated("insecure-port", "This flag will be removed in a future version.")  fs.Lookup("insecure-port").Hidden = false}

此后,--insecure-bind-address--insecure-port 这两个 Flag 还能进行使用,但官方团队建议用户应尽早弃用,因为他们认为任何服务都应该先通过安全认证,才能进行使用。

于是,--insecure-bind-address--insecure-port 就相当于被宣判了“死缓”。

unsetunset0x04 不安全的服务被关闭unsetunset

时间来到 2020 年 12 月 9 日,Kubernetes 发布的 v1.20.0 版本在根据默认配置创建 API Server 服务的时候,就删除了创建不安全的服务的代码。

// NewServerRunOptions creates a new ServerRunOptions object with default parameters// 代码片段链接:https://github.com/kubernetes/kubernetes/blob/4a89df5617b8e1e26abb16150502d04e6c180533/cmd/kube-apiserver/app/options/options.go#L98funcNewServerRunOptions() *ServerRunOptions {  s := ServerRunOptions{    GenericServerRunOptions: genericoptions.NewServerRunOptions(),    Etcd:                    genericoptions.NewEtcdOptions(storagebackend.NewDefaultConfig(kubeoptions.DefaultEtcdPathPrefix, nil)),    SecureServing:           kubeoptions.NewSecureServingOptions(),// 在此处,删除了不安全服务创建的代码    Audit:                   genericoptions.NewAuditOptions(),    Features:                genericoptions.NewFeatureOptions(),    Admission:               kubeoptions.NewAdmissionOptions(),    Authentication:          kubeoptions.NewBuiltInAuthenticationOptions().WithAll(),    Authorization:           kubeoptions.NewBuiltInAuthorizationOptions(),    CloudProvider:           kubeoptions.NewCloudProviderOptions(),    APIEnablement:           genericoptions.NewAPIEnablementOptions(),    EgressSelector:          genericoptions.NewEgressSelectorOptions(),    Metrics:                 metrics.NewOptions(),    Logs:                    logs.NewOptions(),    EnableLogsHandler:                 true,    EventTTL:                          1 * time.Hour,    MasterCount:                       1,    EndpointReconcilerType:            string(reconcilers.LeaseEndpointReconcilerType),    IdentityLeaseDurationSeconds:      3600,    IdentityLeaseRenewIntervalSeconds: 10,    KubeletConfig: kubeletclient.KubeletClientConfig{      Port:         ports.KubeletPort,      ReadOnlyPort: ports.KubeletReadOnlyPort,      PreferredAddressTypes: []string{// --override-hostnamestring(api.NodeHostName),// internal, preferring DNS if reportedstring(api.NodeInternalDNS),string(api.NodeInternalIP),// external, preferring DNS if reportedstring(api.NodeExternalDNS),string(api.NodeExternalIP),      },      HTTPTimeout: time.Duration(5) * time.Second,    },    ServiceNodePortRange: kubeoptions.DefaultServiceNodePortRange,  }// Overwrite the default for storage data format.  s.Etcd.DefaultStorageMediaType = "application/vnd.kubernetes.protobuf"return &s}

并且--insecure-port 只能设置为0,表示关闭不安全的服务。

// TODO: delete this check after insecure flags removed in v1.24// 代码片段链接:https://github.com/kubernetes/kubernetes/blob/4a89df5617b8e1e26abb16150502d04e6c180533/cmd/kube-apiserver/app/server.go#L92funccheckNonZeroInsecurePort(fs *pflag.FlagSet)error {for _, name := range options.InsecurePortFlags {    val, err := fs.GetInt(name)if err != nil {return err    }if val != 0 {return fmt.Errorf("invalid port value %d: only zero is allowed", val)    }  }returnnil}

如果强行将--insecure-port 标志设置为非 0 端口,例如 8080,API Server 将会报错无法提供服务。只有将--insecure-port 改回为 0 后,APIserver 恢复正常。

API Server 安全风险(一):不安全的服务(理论篇)
图7:--insecure-port 设置为非 0 端口,API Server 会报错
API Server 安全风险(一):不安全的服务(理论篇)
图8:--insecure-port 设置为 0 端口后,API Server 恢复正常

通过限制端口的配置,Kubernetes 无需修改--insecure-bind-address 的相关代码,即可关闭不安全的服务。

所以,Kubernetes 自 v1.20.0 版本之后,--insecure-bind-address--insecure-port 这两个 Flag 将不再起任何作用了。

// TODO: remove these insecure flags in v1.24// 代码片段链接:https://github.com/kubernetes/kubernetes/blob/4a89df5617b8e1e26abb16150502d04e6c180533/cmd/kube-apiserver/app/options/options.go#L146funcaddDummyInsecureFlags(fs *pflag.FlagSet) {var (    bindAddr = net.IPv4(127, 0, 0, 1)    bindPort int  )for _, name := range []string{"insecure-bind-address", "address"} {    fs.IPVar(&bindAddr, name, bindAddr, ""+"The IP address on which to serve the insecure port (set to 0.0.0.0 for all IPv4 interfaces and :: for all IPv6 interfaces).")    fs.MarkDeprecated(name, "This flag has no effect now and will be removed in v1.24.")  }for _, name := range InsecurePortFlags {    fs.IntVar(&bindPort, name, bindPort, ""+"The port on which to serve unsecured, unauthenticated access.")    fs.MarkDeprecated(name, "This flag has no effect now and will be removed in v1.24.")  }}

unsetunset0x05 不安全的服务被移除unsetunset

2022 年 5 月 25 日,Kubernetes 发布了 v1.24.0 版本,在这次的发布信息中,Kubernetes 终于将--insecure-bind-address--insecure-port 两个 Flag 完全移除。

若您还在使用这两个 Flag,API Server 将报错、无法启动。至此,不安全的服务将完全退出历史舞台。

API Server 安全风险(一):不安全的服务(理论篇)
图9:Kubernetes v1.24 的Change Log

unsetunset0x06 总结unsetunset

本篇文章,我们讨论了 API Server 不安全服务的前世今生。在实际工作和学习中,我们只需要记住一点:任何不谈及 Kubernetes 版本的安全风险指南都是耍流氓

当我们获取到一个 Kubernetes 集群时,需要关注其版本信息:

  • 2018 年 3 月 7 日,Kubernetes 发布了 v1.10.0 版本,宣称弃用--insecure-bind-address--insecure-port 这两个标志,这相当于被宣判了“死缓”,因为我们仍然可以使用这两个 Flag,将 API Server 配置为可远程访问。

  • 2020 年 12 月 9 日,Kubernetes 发布了 v1.20.0 版本,在这之后,您无法将--insecure-port 的设置为非 0 值,因为 API Server 不安全服务已关闭。

  • 2022 年 5 月 25 日,Kubernetes 发布了 v1.24.0 版本,API Server 不安全服务已完全退出历史舞台。

说到这里,你是否需要去看看自己的集群是哪个版本呢?

API Server 安全风险(一):不安全的服务(理论篇)
图10:总结

Kubernetes 是一个非常著名的开源项目,它结合了来自世界各地的开源爱好者的智慧和力量,不断的进行更新与迭代,也许在今天或明天,就解决了某个安全风险、或引入新的安全风险。

不安全的服务所带来的风险被完全解决了,但安全的服务未必始终是安全的。

下一讲,我们来说说 Kubernetes 开放在 6443 端口的安全服务,敬请期待~

原文始发于微信公众号(喵苗安全):API Server 安全风险(一):不安全的服务(理论篇)

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年12月5日22:19:26
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   API Server 安全风险(一):不安全的服务(理论篇)https://cn-sec.com/archives/3472917.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息