github.com/crowdsecurity/crowdsec@v1.6.1/pkg/acquisition/modules/kubernetesaudit/k8s_audit.go (about) 1 package kubernetesauditacquisition 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "io" 8 "net/http" 9 "strings" 10 11 "github.com/prometheus/client_golang/prometheus" 12 log "github.com/sirupsen/logrus" 13 "gopkg.in/tomb.v2" 14 "gopkg.in/yaml.v2" 15 "k8s.io/apiserver/pkg/apis/audit" 16 17 "github.com/crowdsecurity/go-cs-lib/trace" 18 19 "github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration" 20 "github.com/crowdsecurity/crowdsec/pkg/types" 21 ) 22 23 type KubernetesAuditConfiguration struct { 24 ListenAddr string `yaml:"listen_addr"` 25 ListenPort int `yaml:"listen_port"` 26 WebhookPath string `yaml:"webhook_path"` 27 configuration.DataSourceCommonCfg `yaml:",inline"` 28 } 29 30 type KubernetesAuditSource struct { 31 metricsLevel int 32 config KubernetesAuditConfiguration 33 logger *log.Entry 34 mux *http.ServeMux 35 server *http.Server 36 outChan chan types.Event 37 addr string 38 } 39 40 var eventCount = prometheus.NewCounterVec( 41 prometheus.CounterOpts{ 42 Name: "cs_k8sauditsource_hits_total", 43 Help: "Total number of events received by k8s-audit source", 44 }, 45 []string{"source"}) 46 47 var requestCount = prometheus.NewCounterVec( 48 prometheus.CounterOpts{ 49 Name: "cs_k8sauditsource_requests_total", 50 Help: "Total number of requests received", 51 }, 52 []string{"source"}) 53 54 func (ka *KubernetesAuditSource) GetUuid() string { 55 return ka.config.UniqueId 56 } 57 58 func (ka *KubernetesAuditSource) GetMetrics() []prometheus.Collector { 59 return []prometheus.Collector{eventCount, requestCount} 60 } 61 62 func (ka *KubernetesAuditSource) GetAggregMetrics() []prometheus.Collector { 63 return []prometheus.Collector{eventCount, requestCount} 64 } 65 66 func (ka *KubernetesAuditSource) UnmarshalConfig(yamlConfig []byte) error { 67 k8sConfig := KubernetesAuditConfiguration{} 68 err := yaml.UnmarshalStrict(yamlConfig, &k8sConfig) 69 if err != nil { 70 return fmt.Errorf("cannot parse k8s-audit configuration: %w", err) 71 } 72 73 ka.config = k8sConfig 74 75 if ka.config.ListenAddr == "" { 76 return fmt.Errorf("listen_addr cannot be empty") 77 } 78 79 if ka.config.ListenPort == 0 { 80 return fmt.Errorf("listen_port cannot be empty") 81 } 82 83 if ka.config.WebhookPath == "" { 84 return fmt.Errorf("webhook_path cannot be empty") 85 } 86 87 if ka.config.WebhookPath[0] != '/' { 88 ka.config.WebhookPath = "/" + ka.config.WebhookPath 89 } 90 91 if ka.config.Mode == "" { 92 ka.config.Mode = configuration.TAIL_MODE 93 } 94 return nil 95 } 96 97 func (ka *KubernetesAuditSource) Configure(config []byte, logger *log.Entry, MetricsLevel int) error { 98 ka.logger = logger 99 ka.metricsLevel = MetricsLevel 100 101 err := ka.UnmarshalConfig(config) 102 if err != nil { 103 return err 104 } 105 106 ka.logger.Tracef("K8SAudit configuration: %+v", ka.config) 107 108 ka.addr = fmt.Sprintf("%s:%d", ka.config.ListenAddr, ka.config.ListenPort) 109 110 ka.mux = http.NewServeMux() 111 112 ka.server = &http.Server{ 113 Addr: ka.addr, 114 Handler: ka.mux, 115 } 116 117 ka.mux.HandleFunc(ka.config.WebhookPath, ka.webhookHandler) 118 return nil 119 } 120 121 func (ka *KubernetesAuditSource) ConfigureByDSN(dsn string, labels map[string]string, logger *log.Entry, uuid string) error { 122 return fmt.Errorf("k8s-audit datasource does not support command-line acquisition") 123 } 124 125 func (ka *KubernetesAuditSource) GetMode() string { 126 return ka.config.Mode 127 } 128 129 func (ka *KubernetesAuditSource) GetName() string { 130 return "k8s-audit" 131 } 132 133 func (ka *KubernetesAuditSource) OneShotAcquisition(out chan types.Event, t *tomb.Tomb) error { 134 return fmt.Errorf("k8s-audit datasource does not support one-shot acquisition") 135 } 136 137 func (ka *KubernetesAuditSource) StreamingAcquisition(out chan types.Event, t *tomb.Tomb) error { 138 ka.outChan = out 139 t.Go(func() error { 140 defer trace.CatchPanic("crowdsec/acquis/k8s-audit/live") 141 ka.logger.Infof("Starting k8s-audit server on %s:%d%s", ka.config.ListenAddr, ka.config.ListenPort, ka.config.WebhookPath) 142 t.Go(func() error { 143 err := ka.server.ListenAndServe() 144 if err != nil && err != http.ErrServerClosed { 145 return fmt.Errorf("k8s-audit server failed: %w", err) 146 } 147 return nil 148 }) 149 <-t.Dying() 150 ka.logger.Infof("Stopping k8s-audit server on %s:%d%s", ka.config.ListenAddr, ka.config.ListenPort, ka.config.WebhookPath) 151 ka.server.Shutdown(context.TODO()) 152 return nil 153 }) 154 return nil 155 } 156 157 func (ka *KubernetesAuditSource) CanRun() error { 158 return nil 159 } 160 161 func (ka *KubernetesAuditSource) Dump() interface{} { 162 return ka 163 } 164 165 func (ka *KubernetesAuditSource) webhookHandler(w http.ResponseWriter, r *http.Request) { 166 167 if ka.metricsLevel != configuration.METRICS_NONE { 168 requestCount.WithLabelValues(ka.addr).Inc() 169 } 170 if r.Method != http.MethodPost { 171 w.WriteHeader(http.StatusMethodNotAllowed) 172 return 173 } 174 ka.logger.Tracef("webhookHandler called") 175 var auditEvents audit.EventList 176 177 jsonBody, err := io.ReadAll(r.Body) 178 if err != nil { 179 ka.logger.Errorf("Error reading request body: %v", err) 180 w.WriteHeader(http.StatusInternalServerError) 181 return 182 } 183 ka.logger.Tracef("webhookHandler receveid: %s", string(jsonBody)) 184 err = json.Unmarshal(jsonBody, &auditEvents) 185 if err != nil { 186 ka.logger.Errorf("Error decoding audit events: %s", err) 187 w.WriteHeader(http.StatusInternalServerError) 188 return 189 } 190 191 remoteIP := strings.Split(r.RemoteAddr, ":")[0] 192 for _, auditEvent := range auditEvents.Items { 193 if ka.metricsLevel != configuration.METRICS_NONE { 194 eventCount.WithLabelValues(ka.addr).Inc() 195 } 196 bytesEvent, err := json.Marshal(auditEvent) 197 if err != nil { 198 ka.logger.Errorf("Error marshaling audit event: %s", err) 199 continue 200 } 201 ka.logger.Tracef("Got audit event: %s", string(bytesEvent)) 202 l := types.Line{ 203 Raw: string(bytesEvent), 204 Labels: ka.config.Labels, 205 Time: auditEvent.StageTimestamp.Time, 206 Src: remoteIP, 207 Process: true, 208 Module: ka.GetName(), 209 } 210 ka.outChan <- types.Event{ 211 Line: l, 212 Process: true, 213 Type: types.LOG, 214 ExpectMode: types.LIVE, 215 } 216 } 217 }