if w !=nil{if evaluator ==nil{
klog.V(2).Info("No audit policy file provided, no events will be recorded for log backend")}else{
logBackend = o.LogOptions.newBackend(w)}}
3 根据配置构建webhook的 后端
// 3. Build webhook backendvar webhookBackend audit.Backend
if o.WebhookOptions.enabled(){if evaluator ==nil{
klog.V(2).Info("No audit policy file provided, no events will be recorded for webhook backend")}else{if c.EgressSelector !=nil{var egressDialer utilnet.DialFunc
egressDialer, err = c.EgressSelector.Lookup(egressselector.ControlPlane.AsNetworkContext())if err !=nil{return err
}
webhookBackend, err = o.WebhookOptions.newUntruncatedBackend(egressDialer)}else{
webhookBackend, err = o.WebhookOptions.newUntruncatedBackend(nil)}if err !=nil{return err
}}}
4 如果有webhook就把它封装为dynamicBackend
// 4. Apply dynamic options.var dynamicBackend audit.Backend
if webhookBackend !=nil{// if only webhook is enabled wrap it in the truncate options
dynamicBackend = o.WebhookOptions.TruncateOptions.wrapBackend(webhookBackend, groupVersion)}
5. 设置审计的策略计算对象 evaluator
// 5. Set the policy rule evaluator
c.AuditPolicyRuleEvaluator = evaluator
6 把logBackend和 dynamicBackend 做union
// 6. Join the log backend with the webhooks
c.AuditBackend =appendBackend(logBackend, dynamicBackend)funcappendBackend(existing, newBackend audit.Backend) audit.Backend {if existing ==nil{return newBackend
}if newBackend ==nil{return existing
}return audit.Union(existing, newBackend)}
type Sink interface{// ProcessEvents handles events. Per audit ID it might be that ProcessEvents is called up to three times.// Errors might be logged by the sink itself. If an error should be fatal, leading to an internal// error, ProcessEvents is supposed to panic. The event must not be mutated and is reused by the caller// after the call returns, i.e. the sink has to make a deepcopy to keep a copy around if necessary.// Returns true on success, may return false on error.ProcessEvents(events ...*auditinternal.Event)bool}type Backend interface{
Sink
// Run will initialize the backend. It must not block, but may run go routines in the background. If// stopCh is closed, it is supposed to stop them. Run will be called before the first call to ProcessEvents.Run(stopCh <-chanstruct{})error// Shutdown will synchronously shut down the backend while making sure that all pending// events are delivered. It can be assumed that this method is called after// the stopCh channel passed to the Run method has been closed.Shutdown()// Returns the backend PluginName.String()string}
// WithAudit decorates a http.Handler with audit logging information for all the// requests coming to the server. Audit level is decided according to requests'// attributes and audit policy. Logs are emitted to the audit sink to// process events. If sink or audit policy is nil, no decoration takes place.funcWithAudit(handler http.Handler, sink audit.Sink, policy audit.PolicyRuleEvaluator, longRunningCheck request.LongRunningRequestCheck) http.Handler {if sink ==nil|| policy ==nil{return handler
}return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request){
req, ev, omitStages, err :=createAuditEventAndAttachToContext(req, policy)if err !=nil{
utilruntime.HandleError(fmt.Errorf("failed to create audit event: %v", err))
responsewriters.InternalError(w, req, errors.New("failed to create audit event"))return}
ctx := req.Context()if ev ==nil|| ctx ==nil{
handler.ServeHTTP(w, req)return}
ev.Stage = auditinternal.StageRequestReceived
if processed :=processAuditEvent(ctx, sink, ev, omitStages);!processed {
audit.ApiserverAuditDroppedCounter.WithContext(ctx).Inc()
responsewriters.InternalError(w, req, errors.New("failed to store audit event"))return}// intercept the status codevar longRunningSink audit.Sink
if longRunningCheck !=nil{
ri,_:= request.RequestInfoFrom(ctx)iflongRunningCheck(req, ri){
longRunningSink = sink
}}
respWriter :=decorateResponseWriter(ctx, w, ev, longRunningSink, omitStages)// send audit event when we leave this func, either via a panic or cleanly. In the case of long// running requests, this will be the second audit event.deferfunc(){if r :=recover(); r !=nil{deferpanic(r)
ev.Stage = auditinternal.StagePanic
ev.ResponseStatus =&metav1.Status{
Code: http.StatusInternalServerError,
Status: metav1.StatusFailure,
Reason: metav1.StatusReasonInternalError,
Message: fmt.Sprintf("APIServer panic'd: %v", r),}processAuditEvent(ctx, sink, ev, omitStages)return}// if no StageResponseStarted event was sent b/c neither a status code nor a body was sent, fake it here// But Audit-Id http header will only be sent when http.ResponseWriter.WriteHeader is called.
fakedSuccessStatus :=&metav1.Status{
Code: http.StatusOK,
Status: metav1.StatusSuccess,
Message:"Connection closed early",}if ev.ResponseStatus ==nil&& longRunningSink !=nil{
ev.ResponseStatus = fakedSuccessStatus
ev.Stage = auditinternal.StageResponseStarted
processAuditEvent(ctx, longRunningSink, ev, omitStages)}
ev.Stage = auditinternal.StageResponseComplete
if ev.ResponseStatus ==nil{
ev.ResponseStatus = fakedSuccessStatus
}processAuditEvent(ctx, sink, ev, omitStages)}()
handler.ServeHTTP(respWriter, req)})}
audit审计的总结
Kubernetes 审计(Auditing) 功能提供了与安全相关的、按时间顺序排列的记录集,记录每个用户、使用 Kubernetes API 的应用以及控制面自身引发的活动