diff --git a/apis/logging/v1beta1/collector_config_types.go b/apis/logging/v1beta1/collector_config_types.go index 95f7a20982..8c79661708 100644 --- a/apis/logging/v1beta1/collector_config_types.go +++ b/apis/logging/v1beta1/collector_config_types.go @@ -49,6 +49,12 @@ type RKE2Spec struct { LogPath string `json:"logPath,omitempty"` } +type KubeAuditLogsSpec struct { + Enabled bool `json:"enabled,omitempty"` + AuditFilename string `json:"auditFilename,omitempty"` + PathPrefix string `json:"pathPrefix,omitempty"` +} + // CollectorConfigSpec defines the desired state of CollectorConfig type CollectorConfigSpec struct { // +kubebuilder:validation:Enum:=aks;eks;gke;k3s;rke;rke2;generic @@ -64,6 +70,8 @@ type CollectorConfigSpec struct { K3S *K3SSpec `json:"k3s,omitempty"` RKE *RKESpec `json:"rke,omitempty"` RKE2 *RKE2Spec `json:"rke2,omitempty"` + + KubeAuditLogs *KubeAuditLogsSpec `json:"kubeAuditLogs,omitempty"` } type SelectorConfig struct { diff --git a/apis/logging/v1beta1/zz_generated.deepcopy.go b/apis/logging/v1beta1/zz_generated.deepcopy.go index c54ed61fad..df739512e0 100644 --- a/apis/logging/v1beta1/zz_generated.deepcopy.go +++ b/apis/logging/v1beta1/zz_generated.deepcopy.go @@ -134,6 +134,11 @@ func (in *CollectorConfigSpec) DeepCopyInto(out *CollectorConfigSpec) { *out = new(RKE2Spec) **out = **in } + if in.KubeAuditLogs != nil { + in, out := &in.KubeAuditLogs, &out.KubeAuditLogs + *out = new(KubeAuditLogsSpec) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CollectorConfigSpec. @@ -354,6 +359,21 @@ func (in *K3SSpec) DeepCopy() *K3SSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeAuditLogsSpec) DeepCopyInto(out *KubeAuditLogsSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeAuditLogsSpec. +func (in *KubeAuditLogsSpec) DeepCopy() *KubeAuditLogsSpec { + if in == nil { + return nil + } + out := new(KubeAuditLogsSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LoggingClusterBinding) DeepCopyInto(out *LoggingClusterBinding) { *out = *in diff --git a/config/crd/bases/logging.opni.io_collectorconfigs.yaml b/config/crd/bases/logging.opni.io_collectorconfigs.yaml index 662ab302dd..0773be6590 100644 --- a/config/crd/bases/logging.opni.io_collectorconfigs.yaml +++ b/config/crd/bases/logging.opni.io_collectorconfigs.yaml @@ -39,6 +39,15 @@ spec: logPath: type: string type: object + kubeAuditLogs: + properties: + auditFilename: + type: string + enabled: + type: boolean + pathPrefix: + type: string + type: object provider: enum: - aks diff --git a/pkg/resources/collector/logging.go b/pkg/resources/collector/logging.go index 8e7e771925..2ddcf11d4e 100644 --- a/pkg/resources/collector/logging.go +++ b/pkg/resources/collector/logging.go @@ -2,22 +2,16 @@ package collector import ( "bytes" + "path/filepath" opniloggingv1beta1 "github.com/rancher/opni/apis/logging/v1beta1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" ) -func (r *Reconciler) generateDistributionReceiver() (receiver []string, retBytes []byte, retErr error) { - config := &opniloggingv1beta1.CollectorConfig{} - retErr = r.client.Get(r.ctx, types.NamespacedName{ - Name: r.collector.Spec.LoggingConfig.Name, - Namespace: r.collector.Spec.SystemNamespace, - }, config) - if retErr != nil { - return - } +func (r *Reconciler) generateDistributionReceiver(config *opniloggingv1beta1.CollectorConfig) (receiver []string, retBytes []byte, retErr error) { var providerReceiver bytes.Buffer + switch config.Spec.Provider { case opniloggingv1beta1.LogProviderRKE: return []string{logReceiverRKE}, []byte(templateLogAgentRKE), nil @@ -49,6 +43,31 @@ func (r *Reconciler) generateDistributionReceiver() (receiver []string, retBytes } } +func (r *Reconciler) generateKubeAuditLogsReceiver(config *opniloggingv1beta1.CollectorConfig) (string, []byte, error) { + var receiver bytes.Buffer + + if config.Spec.KubeAuditLogs != nil && config.Spec.KubeAuditLogs.Enabled { + filelogDir := "/var/log/kube-audit" + if config.Spec.KubeAuditLogs.PathPrefix != "" { + filelogDir = config.Spec.KubeAuditLogs.PathPrefix + } + + auditLogFilename := "audit.log" + if config.Spec.KubeAuditLogs.AuditFilename != "" { + auditLogFilename = config.Spec.KubeAuditLogs.AuditFilename + } + + err := templateKubeAuditLogs.Execute(&receiver, filepath.Join(filelogDir, auditLogFilename)) + if err != nil { + return "", nil, err + } + + return logReceiverKubeAuditLogs, receiver.Bytes(), nil + } + + return "", nil, nil +} + func (r *Reconciler) hostLoggingVolumes() ( retVolumeMounts []corev1.VolumeMount, retVolumes []corev1.Volume, @@ -90,6 +109,26 @@ func (r *Reconciler) hostLoggingVolumes() ( }, }, }) + + kubeAuditLogsDir := "/var/log/kube-audit" + if config.Spec.KubeAuditLogs != nil && config.Spec.KubeAuditLogs.PathPrefix != "" { + kubeAuditLogsDir = config.Spec.KubeAuditLogs.PathPrefix + } + + retVolumeMounts = append(retVolumeMounts, corev1.VolumeMount{ + Name: "kubeauditlogs", + MountPath: kubeAuditLogsDir, + ReadOnly: true, + }) + retVolumes = append(retVolumes, corev1.Volume{ + Name: "kubeauditlogs", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: kubeAuditLogsDir, + }, + }, + }) + switch config.Spec.Provider { case opniloggingv1beta1.LogProviderRKE: retVolumeMounts = append(retVolumeMounts, corev1.VolumeMount{ diff --git a/pkg/resources/collector/templates.go b/pkg/resources/collector/templates.go index 200c0f4549..f24cfd4ee6 100644 --- a/pkg/resources/collector/templates.go +++ b/pkg/resources/collector/templates.go @@ -5,11 +5,12 @@ import ( ) const ( - logReceiverK8s = "filelog/k8s" - logReceiverRKE = "filelog/rke" - logReceiverK3s = "journald/k3s" - logReceiverRKE2 = "journald/rke2" - fileLogReceiverRKE2 = "filelog/rke2" + logReceiverK8s = "filelog/k8s" + logReceiverKubeAuditLogs = "filelog/kubeauditlogs" + logReceiverRKE = "filelog/rke" + logReceiverK3s = "journald/k3s" + logReceiverRKE2 = "journald/rke2" + fileLogReceiverRKE2 = "filelog/rke2" ) var ( @@ -101,6 +102,53 @@ journald/k3s: units: [ "k3s" ] directory: {{ . }} `)) + + templateKubeAuditLogs = template.Must(template.New("kubeauditlogsreceiver").Parse(` +filelog/kubeauditlogs: + include: [ {{ . }} ] + start_at: beginning + include_file_path: false + include_file_name: false + operators: + - type: json_parser + id: parse-body + timestamp: + parse_from: attributes.stageTimestamp + layout: '%Y-%m-%dT%H:%M:%S.%LZ' + - type: add + field: attributes.log_type + value: auditlog + - type: add + field: attributes.kubernetes_component + value: kubeauditlogs + - type: move + from: attributes.stage + to: resource["k8s.auditlog.stage"] + - type: move + from: attributes.stageTimestamp + to: resource["k8s.auditlog.stage_timestamp"] + - type: move + from: attributes.level + to: resource["k8s.auditlog.level"] + - type: move + from: attributes.auditID + to: resource["k8s.auditlog.audit_id"] + - type: move + from: attributes.objectRef.resource + to: resource["k8s.auditlog.resource"] + - type: retain + fields: + - attributes.stage + - attributes.stageTimestamp + - attributes.level + - attributes.auditID + - attributes.objectRef.resource + - attributes.cluster_id + - attributes.time + - attributes.log + - attributes.log_type +`)) + templateLogAgentRKE2 = template.Must(template.New("rke2receiver").Parse(` journald/rke2: units: diff --git a/pkg/resources/collector/workloads.go b/pkg/resources/collector/workloads.go index 240e3d8580..5d9ea7c48a 100644 --- a/pkg/resources/collector/workloads.go +++ b/pkg/resources/collector/workloads.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "fmt" + opniloggingv1beta1 "github.com/rancher/opni/apis/logging/v1beta1" monitoringv1beta1 "github.com/rancher/opni/apis/monitoring/v1beta1" "github.com/rancher/opni/pkg/otel" "github.com/rancher/opni/pkg/resources" @@ -14,6 +15,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -36,9 +38,7 @@ const ( machineID = "/etc/machine-id" ) -var ( - directoryOrCreate = corev1.HostPathDirectoryOrCreate -) +var directoryOrCreate = corev1.HostPathDirectoryOrCreate func (r *Reconciler) agentConfigMapName() string { return fmt.Sprintf("%s-agent-config", r.collector.Name) @@ -78,7 +78,23 @@ func (r *Reconciler) receiverConfig() (retData []byte, retReceivers []string, re retData = append(retData, []byte(templateLogAgentK8sReceiver)...) retReceivers = append(retReceivers, logReceiverK8s) - receiver, data, err := r.generateDistributionReceiver() + config, err := r.fetchLoggingCollectorConfig() + if err != nil { + retErr = err + return + } + + auditLogsReceiver, data, err := r.generateKubeAuditLogsReceiver(config) + if err != nil { + retErr = err + return + } + retData = append(retData, data...) + if len(auditLogsReceiver) > 0 { + retReceivers = append(retReceivers, auditLogsReceiver) + } + + receiver, data, err := r.generateDistributionReceiver(config) if err != nil { retErr = err return @@ -97,6 +113,7 @@ func (r *Reconciler) receiverConfig() (retData []byte, retReceivers []string, re } retData = append(retData, data...) } + return } @@ -559,3 +576,13 @@ func (r *Reconciler) configReloaderImageSpec() opnimeta.ImageSpec { }(), }.Resolve() } + +func (r *Reconciler) fetchLoggingCollectorConfig() (*opniloggingv1beta1.CollectorConfig, error) { + config := &opniloggingv1beta1.CollectorConfig{} + err := r.client.Get(r.ctx, types.NamespacedName{ + Name: r.collector.Spec.LoggingConfig.Name, + Namespace: r.collector.Spec.SystemNamespace, + }, config) + + return config, err +} diff --git a/plugins/logging/pkg/agent/drivers/kubernetes_manager/kubernetes_manager.go b/plugins/logging/pkg/agent/drivers/kubernetes_manager/kubernetes_manager.go index 541439c21e..ee2ce87648 100644 --- a/plugins/logging/pkg/agent/drivers/kubernetes_manager/kubernetes_manager.go +++ b/plugins/logging/pkg/agent/drivers/kubernetes_manager/kubernetes_manager.go @@ -152,6 +152,9 @@ func (m *KubernetesManagerDriver) buildLoggingCollectorConfig() *opniloggingv1be }, Spec: opniloggingv1beta1.CollectorConfigSpec{ Provider: opniloggingv1beta1.LogProvider(m.provider), + KubeAuditLogs: &opniloggingv1beta1.KubeAuditLogsSpec{ + Enabled: true, + }, }, } return collectorConfig