Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Collect audit logs from kube-apiserver #1666

Merged
merged 12 commits into from
Sep 22, 2023
8 changes: 8 additions & 0 deletions apis/logging/v1beta1/collector_config_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down
20 changes: 20 additions & 0 deletions apis/logging/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions config/crd/bases/logging.opni.io_collectorconfigs.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 48 additions & 9 deletions pkg/resources/collector/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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{
Expand Down
58 changes: 53 additions & 5 deletions pkg/resources/collector/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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"]
dbason marked this conversation as resolved.
Show resolved Hide resolved
- 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:
Expand Down
35 changes: 31 additions & 4 deletions pkg/resources/collector/workloads.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -97,6 +113,7 @@ func (r *Reconciler) receiverConfig() (retData []byte, retReceivers []string, re
}
retData = append(retData, data...)
}

return
}

Expand Down Expand Up @@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ func (m *KubernetesManagerDriver) buildLoggingCollectorConfig() *opniloggingv1be
},
Spec: opniloggingv1beta1.CollectorConfigSpec{
Provider: opniloggingv1beta1.LogProvider(m.provider),
KubeAuditLogs: &opniloggingv1beta1.KubeAuditLogsSpec{
Enabled: true,
},
},
}
return collectorConfig
Expand Down