kubernetes 审计日志功能

审计日志可以记录所有对 apiserver 接口的调用,让我们能够非常清晰的知道集群到底发生了什么事情,通过记录的日志可以查到所发生的事件、操作的用户和时间。kubernetes 在 v1.7 中支持了日志审计功能(Alpha),在 v1.8 中为 Beta 版本,v1.12 为 GA 版本。

kubernetes feature-gates 中的功能 Alpha 版本默认为 false,到 Beta 版本时默认为 true,所以 v1.8 会默认启用审计日志的功能。

一、审计日志的策略

1、日志记录阶段

kube-apiserver 是负责接收及相应用户请求的一个组件,每一个请求都会有几个阶段,每个阶段都有对应的日志,当前支持的阶段有:

  • RequestReceived - apiserver 在接收到请求后且在将该请求下发之前会生成对应的审计日志。
  • ResponseStarted - 在响应 header 发送后并在响应 body 发送前生成日志。这个阶段仅为长时间运行的请求生成(例如 watch)。
  • ResponseComplete - 当响应 body 发送完并且不再发送数据。
  • Panic - 当有 panic 发生时生成。

也就是说对 apiserver 的每一个请求理论上会有三个阶段的审计日志生成。

2、日志记录级别

当前支持的日志记录级别有:

  • None - 不记录日志。
  • Metadata - 只记录 Request 的一些 metadata (例如 user, timestamp, resource, verb 等),但不记录 Request 或 Response 的body。
  • Request - 记录 Request 的 metadata 和 body。
  • RequestResponse - 最全记录方式,会记录所有的 metadata、Request 和 Response 的 body。

3、日志记录策略

在记录日志的时候尽量只记录所需要的信息,不需要的日志尽可能不记录,避免造成系统资源的浪费。

  • 一个请求不要重复记录,每个请求有三个阶段,只记录其中需要的阶段
  • 不要记录所有的资源,不要记录一个资源的所有子资源
  • 系统的请求不需要记录,kubelet、kube-proxy、kube-scheduler、kube-controller-manager 等对 kube-apiserver 的请求不需要记录
  • 对一些认证信息(secerts、configmaps、token 等)的 body 不记录

k8s 审计日志的一个示例:

{
  "kind": "EventList",
  "apiVersion": "audit.k8s.io/v1beta1",
  "Items": [
    {
      "Level": "Request",
      "AuditID": "793e7ae2-5ca7-4ad3-a632-19708d2f8265",
      "Stage": "RequestReceived",
      "RequestURI": "/api/v1/namespaces/default/pods/test-pre-sf-de7cc-0",
      "Verb": "get",
      "User": {
        "Username": "system:unsecured",
        "UID": "",
        "Groups": [
          "system:masters",
          "system:authenticated"
        ],
        "Extra": null
      },
      "ImpersonatedUser": null,
      "SourceIPs": [
        "192.168.1.11"
      ],
      "UserAgent": "kube-scheduler/v1.12.2 (linux/amd64) kubernetes/73f3294/scheduler",
      "ObjectRef": {
        "Resource": "pods",
        "Namespace": "default",
        "Name": "test-pre-sf-de7cc-0",
        "UID": "",
        "APIGroup": "",
        "APIVersion": "v1",
        "ResourceVersion": "",
        "Subresource": ""
      },
      "ResponseStatus": null,
      "RequestObject": null,
      "ResponseObject": null,
      "RequestReceivedTimestamp": "2019-01-11T06:51:43.528703Z",
      "StageTimestamp": "2019-01-11T06:51:43.528703Z",
      "Annotations": null
    }
    ]
}

二、启用审计日志

当前的审计日志支持两种收集方式:保存为日志文件和调用自定义的 webhook,在 v1.13 中还支持动态的 webhook。

1、将审计日志以 json 格式保存到本地文件

apiserver 配置文件的 KUBE_API_ARGS 中需要添加如下参数:

--audit-policy-file=/etc/kubernetes/audit-policy.yaml --audit-log-path=/var/log/kube-audit --audit-log-format=json

日志保存到本地后再通过 fluentd 等其他组件进行收集。
还有其他几个选项可以指定保留审计日志文件的最大天数、文件的最大数量、文件的大小等。

2、将审计日志打到后端指定的 webhook

--audit-policy-file=/etc/kubernetes/audit-policy.yaml --audit-webhook-config-file=/etc/kubernetes/audit-webhook-kubeconfig

webhook 配置文件实际上是一个 kubeconfig,apiserver 会将审计日志发送 到指定的 webhook 后,webhook 接收到日志后可以再分发到 kafka 或其他组件进行收集。

audit-webhook-kubeconfig 示例:

apiVersion: v1
clusters:
- cluster:
    server: http://127.0.0.1:8081/audit/webhook
  name: metric
contexts:
- context:
    cluster: metric
    user: ""
  name: default-context
current-context: default-context
kind: Config
preferences: {}
users: []

前面提到过,apiserver 的每一个请求会记录三个阶段的审计日志,但是在实际中并不是需要所有的审计日志,官方也说明了启用审计日志会增加 apiserver 对内存的使用量。

Note: The audit logging feature increases the memory consumption of the API server because some context required for auditing is stored for each request. Additionally, memory consumption depends on the audit logging configuration.

audit-policy.yaml 配置示例:

apiVersion: audit.k8s.io/v1
kind: Policy
# ResponseStarted 阶段不记录
omitStages:
  - "ResponseStarted"
rules:
  # 记录用户对 pod 和 statefulset 的操作
  - level: RequestResponse
    resources:
    - group: ""
      resources: ["pods","pods/status"]
    - group: "apps"
      resources: ["statefulsets","statefulsets/scale"]
  # kube-controller-manager、kube-scheduler 等已经认证过身份的请求不需要记录
  - level: None
    userGroups: ["system:authenticated"]
    nonResourceURLs:
    - "/api*"
    - "/version"
  # 对 config、secret、token 等认证信息不记录请求体和返回体
  - level: Metadata
    resources:
    - group: "" # core API group
      resources: ["secrets", "configmaps"]

官方提供两个参考示例:

3、subresource 说明

kubernetes 每个资源对象都有 subresource,通过调用 master 的 api 可以获取 kubernetes 中所有的 resource 以及对应的 subresource,比如 pod 有 logs、exec 等 subresource。

获取所有 resource( 1.10 之后使用):
$ curl  127.0.0.1:8080/openapi/v2

参考:https://kubernetes.io/docs/concepts/overview/kubernetes-api/

三、webhook 的一个简单示例

package main

import (
    "encoding/json"
    "io/ioutil"
    "log"
    "net/http"

    "github.com/emicklei/go-restful"
    "github.com/gosoon/glog"
    "k8s.io/apiserver/pkg/apis/audit"
)

func main() {
    // NewContainer creates a new Container using a new ServeMux and default router (CurlyRouter)
    container := restful.NewContainer()
    ws := new(restful.WebService)
    ws.Path("/audit").
        Consumes(restful.MIME_JSON).
        Produces(restful.MIME_JSON)
    ws.Route(ws.POST("/webhook").To(AuditWebhook))

    //WebService ws2被添加到container2中
    container.Add(ws)
    server := &http.Server{
        Addr:    ":8081",
        Handler: container,
    }
    //go consumer()
    log.Fatal(server.ListenAndServe())
}

func AuditWebhook(req *restful.Request, resp *restful.Response) {
    body, err := ioutil.ReadAll(req.Request.Body)
    if err != nil {
        glog.Errorf("read body err is: %v", err)
    }
    var eventList audit.EventList
    err = json.Unmarshal(body, &eventList)
    if err != nil {
        glog.Errorf("unmarshal failed with:%v,body is :\n", err, string(body))
        return
    }
    for _, event := range eventList.Items {
        jsonBytes, err := json.Marshal(event)
        if err != nil {
            glog.Infof("marshal failed with:%v,event is \n %+v", err, event)
        }
        // 消费日志
        asyncProducer(string(jsonBytes))
    }
    resp.AddHeader("Content-Type", "application/json")
    resp.WriteEntity("success")
}

完整代码请参考:https://github.com/gosoon/k8s-audit-webhook

四、总结

本文主要介绍了 kubernetes 的日志审计功能,kubernetes 最近也被爆出多个安全漏洞,安全问题是每个团队不可忽视的,kubernetes 虽然被多数公司用作私有云,但日志审计也是不可或缺的。


参考:
https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/
ttps://kubernetes.io/docs/tasks/debug-application-cluster/audit/
阿里云 Kubernetes 审计日志方案

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270