diff --git a/cmd/security-profiles-operator/main.go b/cmd/security-profiles-operator/main.go index 2b4239b974..da0db149ee 100644 --- a/cmd/security-profiles-operator/main.go +++ b/cmd/security-profiles-operator/main.go @@ -184,6 +184,12 @@ func main() { Value: "", Usage: "the container runtime in the cluster (values: cri-o, containerd, docker)", }, + &cli.BoolFlag{ + Name: "apparmor", + Aliases: []string{"a"}, + Usage: "enable installation of apparmor profiles for spo", + EnvVars: []string{config.AppArmorEnvKey}, + }, }, }, &cli.Command{ @@ -261,7 +267,9 @@ func initLogging(ctx *cli.Context) error { return fmt.Errorf("parse verbosity flag: %w", err) } + ctrl.SetLogger(ctrl.Log.V(int(level))) ctrl.Log.Info(fmt.Sprintf("Set logging verbosity to %d", level)) + return nil } @@ -521,6 +529,7 @@ func runNonRootEnabler(ctx *cli.Context, info *version.Info) error { const component = "non-root-enabler" printInfo(component, info) runtime := ctx.String("runtime") + apparmor := ctx.Bool("apparmor") cfg, err := ctrl.GetConfig() if err != nil { @@ -534,7 +543,7 @@ func runNonRootEnabler(ctx *cli.Context, info *version.Info) error { if err != nil { kubeletDir = config.KubeletDir() } - return nonrootenabler.New().Run(ctrl.Log.WithName(component), runtime, kubeletDir) + return nonrootenabler.New().Run(ctrl.Log.WithName(component), runtime, kubeletDir, apparmor) } func runWebhook(ctx *cli.Context, info *version.Info) error { diff --git a/deploy/base/kustomization.yaml b/deploy/base/kustomization.yaml index 69a9fbedaa..863a50356f 100644 --- a/deploy/base/kustomization.yaml +++ b/deploy/base/kustomization.yaml @@ -17,6 +17,8 @@ configMapGenerator: - profiles/selinuxd.cil - profiles/selinuxrecording.cil - profiles/selinuxd-image-mapping.json + - profiles/spo-apparmor.yaml + - profiles/bpfrecorder-apparmor.yaml name: security-profiles-operator-profile generatorOptions: diff --git a/deploy/base/profiles/bpfrecorder-apparmor.yaml b/deploy/base/profiles/bpfrecorder-apparmor.yaml new file mode 100644 index 0000000000..c4f9a29f0a --- /dev/null +++ b/deploy/base/profiles/bpfrecorder-apparmor.yaml @@ -0,0 +1,54 @@ +apiVersion: security-profiles-operator.x-k8s.io/v1alpha1 +kind: AppArmorProfile +metadata: + labels: + spo.x-k8s.io/container-id: bpf-recorder + name: bpfrecorder-apparmor + namespace: security-profiles-operator +spec: + abstract: + capability: + allowedCapabilities: + - bpf + - chown + - dac_override + - dac_read_search + - mknod + - perfmon + - setgid + - setuid + - sys_admin + - sys_resource + executable: + allowedExecutables: + - /security-profiles-operator + filesystem: + readOnlyPaths: + - / + - /proc/@{pid}/cgroup + - /proc/@{pid}/fd/ + - /proc/@{pid}/maps + - /proc/@{pid}/mountinfo + - /proc/@{pid}/setgroups + - /proc/@{pid}/status + - /proc/@{pid}/uid_map + - /proc/filesystems + - /proc/sys/kernel/cap_last_cap + - /proc/sys/net/core/somaxconn + - /sys/devices/kprobe/type + - /sys/devices/system/cpu/online + - /sys/fs/bpf/ + - /sys/kernel/btf/vmlinux + - /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/id + - /sys/kernel/debug/tracing/events/sched/sched_process_exec/id + - /sys/kernel/debug/tracing/events/sched/sched_process_exit/id + - /sys/kernel/debug/tracing/events/syscalls/sys_enter_socket/id + - /sys/kernel/mm/transparent_hugepage/hpage_pmd_size + - /sys/kernel/security/lsm + readWritePaths: + - /dev/null + network: + allowedProtocols: + allowTcp: true + allowUdp: true + disabled: false diff --git a/deploy/base/profiles/spo-apparmor.yaml b/deploy/base/profiles/spo-apparmor.yaml new file mode 100644 index 0000000000..b4b9b2c57e --- /dev/null +++ b/deploy/base/profiles/spo-apparmor.yaml @@ -0,0 +1,48 @@ +apiVersion: security-profiles-operator.x-k8s.io/v1alpha1 +kind: AppArmorProfile +metadata: + labels: + spo.x-k8s.io/container-id: security-profiles-operator + name: spo-apprmor + namespace: security-profiles-operator +spec: + abstract: + capability: + allowedCapabilities: + - chown + - dac_override + - dac_read_search + - fowner + - fsetid + - mknod + - setgid + - setpcap + - setuid + - sys_admin + executable: + allowedExecutables: + - /security-profiles-operator + filesystem: + readOnlyPaths: + - / + - /opt/spo-profiles/** + - /proc/@{pid}/fd/ + - /proc/@{pid}/maps + - /proc/@{pid}/mountinfo + - /proc/@{pid}/setgroups + - /proc/@{pid}/status + - /proc/@{pid}/uid_map + - /proc/filesystems + - /proc/sys/kernel/cap_last_cap + - /proc/sys/net/core/somaxconn + - /sys/kernel/mm/transparent_hugepage/hpage_pmd_size + readWritePaths: + - /dev/null + - /host/var/lib/kubelet/seccomp/** + - /var/lib/security-profiles-operator/kubelet-config.json + network: + allowedProtocols: + allowTcp: true + allowUdp: true + disabled: false + diff --git a/deploy/helm/templates/static-resources.yaml b/deploy/helm/templates/static-resources.yaml index ef38f41f27..7e16c40eef 100644 --- a/deploy/helm/templates/static-resources.yaml +++ b/deploy/helm/templates/static-resources.yaml @@ -789,6 +789,61 @@ data: } ] } + bpfrecorder-apparmor.yaml: | + apiVersion: security-profiles-operator.x-k8s.io/v1alpha1 + kind: AppArmorProfile + metadata: + labels: + spo.x-k8s.io/container-id: bpf-recorder + name: bpfrecorder-apparmor + namespace: security-profiles-operator + spec: + abstract: + capability: + allowedCapabilities: + - bpf + - chown + - dac_override + - dac_read_search + - mknod + - perfmon + - setgid + - setuid + - sys_admin + - sys_resource + executable: + allowedExecutables: + - /security-profiles-operator + filesystem: + readOnlyPaths: + - / + - /proc/@{pid}/cgroup + - /proc/@{pid}/fd/ + - /proc/@{pid}/maps + - /proc/@{pid}/mountinfo + - /proc/@{pid}/setgroups + - /proc/@{pid}/status + - /proc/@{pid}/uid_map + - /proc/filesystems + - /proc/sys/kernel/cap_last_cap + - /proc/sys/net/core/somaxconn + - /sys/devices/kprobe/type + - /sys/devices/system/cpu/online + - /sys/fs/bpf/ + - /sys/kernel/btf/vmlinux + - /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/id + - /sys/kernel/debug/tracing/events/sched/sched_process_exec/id + - /sys/kernel/debug/tracing/events/sched/sched_process_exit/id + - /sys/kernel/debug/tracing/events/syscalls/sys_enter_socket/id + - /sys/kernel/mm/transparent_hugepage/hpage_pmd_size + - /sys/kernel/security/lsm + readWritePaths: + - /dev/null + network: + allowedProtocols: + allowTcp: true + allowUdp: true + disabled: false security-profiles-operator.json: | { "defaultAction": "SCMP_ACT_ERRNO", @@ -955,6 +1010,55 @@ data: (blockinherit container) (typepermissive process) ) + spo-apparmor.yaml: |+ + apiVersion: security-profiles-operator.x-k8s.io/v1alpha1 + kind: AppArmorProfile + metadata: + labels: + spo.x-k8s.io/container-id: security-profiles-operator + name: spo-apprmor + namespace: security-profiles-operator + spec: + abstract: + capability: + allowedCapabilities: + - chown + - dac_override + - dac_read_search + - fowner + - fsetid + - mknod + - setgid + - setpcap + - setuid + - sys_admin + executable: + allowedExecutables: + - /security-profiles-operator + filesystem: + readOnlyPaths: + - / + - /opt/spo-profiles/** + - /proc/@{pid}/fd/ + - /proc/@{pid}/maps + - /proc/@{pid}/mountinfo + - /proc/@{pid}/setgroups + - /proc/@{pid}/status + - /proc/@{pid}/uid_map + - /proc/filesystems + - /proc/sys/kernel/cap_last_cap + - /proc/sys/net/core/somaxconn + - /sys/kernel/mm/transparent_hugepage/hpage_pmd_size + readWritePaths: + - /dev/null + - /host/var/lib/kubelet/seccomp/** + - /var/lib/security-profiles-operator/kubelet-config.json + network: + allowedProtocols: + allowTcp: true + allowUdp: true + disabled: false + kind: ConfigMap metadata: annotations: diff --git a/deploy/namespace-operator.yaml b/deploy/namespace-operator.yaml index 2ee72f18af..8ce0692ba0 100644 --- a/deploy/namespace-operator.yaml +++ b/deploy/namespace-operator.yaml @@ -3072,6 +3072,61 @@ data: } ] } + bpfrecorder-apparmor.yaml: | + apiVersion: security-profiles-operator.x-k8s.io/v1alpha1 + kind: AppArmorProfile + metadata: + labels: + spo.x-k8s.io/container-id: bpf-recorder + name: bpfrecorder-apparmor + namespace: security-profiles-operator + spec: + abstract: + capability: + allowedCapabilities: + - bpf + - chown + - dac_override + - dac_read_search + - mknod + - perfmon + - setgid + - setuid + - sys_admin + - sys_resource + executable: + allowedExecutables: + - /security-profiles-operator + filesystem: + readOnlyPaths: + - / + - /proc/@{pid}/cgroup + - /proc/@{pid}/fd/ + - /proc/@{pid}/maps + - /proc/@{pid}/mountinfo + - /proc/@{pid}/setgroups + - /proc/@{pid}/status + - /proc/@{pid}/uid_map + - /proc/filesystems + - /proc/sys/kernel/cap_last_cap + - /proc/sys/net/core/somaxconn + - /sys/devices/kprobe/type + - /sys/devices/system/cpu/online + - /sys/fs/bpf/ + - /sys/kernel/btf/vmlinux + - /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/id + - /sys/kernel/debug/tracing/events/sched/sched_process_exec/id + - /sys/kernel/debug/tracing/events/sched/sched_process_exit/id + - /sys/kernel/debug/tracing/events/syscalls/sys_enter_socket/id + - /sys/kernel/mm/transparent_hugepage/hpage_pmd_size + - /sys/kernel/security/lsm + readWritePaths: + - /dev/null + network: + allowedProtocols: + allowTcp: true + allowUdp: true + disabled: false security-profiles-operator.json: | { "defaultAction": "SCMP_ACT_ERRNO", @@ -3238,6 +3293,55 @@ data: (blockinherit container) (typepermissive process) ) + spo-apparmor.yaml: |+ + apiVersion: security-profiles-operator.x-k8s.io/v1alpha1 + kind: AppArmorProfile + metadata: + labels: + spo.x-k8s.io/container-id: security-profiles-operator + name: spo-apprmor + namespace: security-profiles-operator + spec: + abstract: + capability: + allowedCapabilities: + - chown + - dac_override + - dac_read_search + - fowner + - fsetid + - mknod + - setgid + - setpcap + - setuid + - sys_admin + executable: + allowedExecutables: + - /security-profiles-operator + filesystem: + readOnlyPaths: + - / + - /opt/spo-profiles/** + - /proc/@{pid}/fd/ + - /proc/@{pid}/maps + - /proc/@{pid}/mountinfo + - /proc/@{pid}/setgroups + - /proc/@{pid}/status + - /proc/@{pid}/uid_map + - /proc/filesystems + - /proc/sys/kernel/cap_last_cap + - /proc/sys/net/core/somaxconn + - /sys/kernel/mm/transparent_hugepage/hpage_pmd_size + readWritePaths: + - /dev/null + - /host/var/lib/kubelet/seccomp/** + - /var/lib/security-profiles-operator/kubelet-config.json + network: + allowedProtocols: + allowTcp: true + allowUdp: true + disabled: false + kind: ConfigMap metadata: labels: diff --git a/deploy/openshift-dev.yaml b/deploy/openshift-dev.yaml index c7b349b13b..694fa6de79 100644 --- a/deploy/openshift-dev.yaml +++ b/deploy/openshift-dev.yaml @@ -3054,6 +3054,61 @@ data: } ] } + bpfrecorder-apparmor.yaml: | + apiVersion: security-profiles-operator.x-k8s.io/v1alpha1 + kind: AppArmorProfile + metadata: + labels: + spo.x-k8s.io/container-id: bpf-recorder + name: bpfrecorder-apparmor + namespace: security-profiles-operator + spec: + abstract: + capability: + allowedCapabilities: + - bpf + - chown + - dac_override + - dac_read_search + - mknod + - perfmon + - setgid + - setuid + - sys_admin + - sys_resource + executable: + allowedExecutables: + - /security-profiles-operator + filesystem: + readOnlyPaths: + - / + - /proc/@{pid}/cgroup + - /proc/@{pid}/fd/ + - /proc/@{pid}/maps + - /proc/@{pid}/mountinfo + - /proc/@{pid}/setgroups + - /proc/@{pid}/status + - /proc/@{pid}/uid_map + - /proc/filesystems + - /proc/sys/kernel/cap_last_cap + - /proc/sys/net/core/somaxconn + - /sys/devices/kprobe/type + - /sys/devices/system/cpu/online + - /sys/fs/bpf/ + - /sys/kernel/btf/vmlinux + - /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/id + - /sys/kernel/debug/tracing/events/sched/sched_process_exec/id + - /sys/kernel/debug/tracing/events/sched/sched_process_exit/id + - /sys/kernel/debug/tracing/events/syscalls/sys_enter_socket/id + - /sys/kernel/mm/transparent_hugepage/hpage_pmd_size + - /sys/kernel/security/lsm + readWritePaths: + - /dev/null + network: + allowedProtocols: + allowTcp: true + allowUdp: true + disabled: false security-profiles-operator.json: | { "defaultAction": "SCMP_ACT_ERRNO", @@ -3220,6 +3275,55 @@ data: (blockinherit container) (typepermissive process) ) + spo-apparmor.yaml: |+ + apiVersion: security-profiles-operator.x-k8s.io/v1alpha1 + kind: AppArmorProfile + metadata: + labels: + spo.x-k8s.io/container-id: security-profiles-operator + name: spo-apprmor + namespace: security-profiles-operator + spec: + abstract: + capability: + allowedCapabilities: + - chown + - dac_override + - dac_read_search + - fowner + - fsetid + - mknod + - setgid + - setpcap + - setuid + - sys_admin + executable: + allowedExecutables: + - /security-profiles-operator + filesystem: + readOnlyPaths: + - / + - /opt/spo-profiles/** + - /proc/@{pid}/fd/ + - /proc/@{pid}/maps + - /proc/@{pid}/mountinfo + - /proc/@{pid}/setgroups + - /proc/@{pid}/status + - /proc/@{pid}/uid_map + - /proc/filesystems + - /proc/sys/kernel/cap_last_cap + - /proc/sys/net/core/somaxconn + - /sys/kernel/mm/transparent_hugepage/hpage_pmd_size + readWritePaths: + - /dev/null + - /host/var/lib/kubelet/seccomp/** + - /var/lib/security-profiles-operator/kubelet-config.json + network: + allowedProtocols: + allowTcp: true + allowUdp: true + disabled: false + kind: ConfigMap metadata: labels: diff --git a/deploy/openshift-downstream.yaml b/deploy/openshift-downstream.yaml index d13a29fb2e..93d5ae2004 100644 --- a/deploy/openshift-downstream.yaml +++ b/deploy/openshift-downstream.yaml @@ -3085,6 +3085,61 @@ data: } ] } + bpfrecorder-apparmor.yaml: | + apiVersion: security-profiles-operator.x-k8s.io/v1alpha1 + kind: AppArmorProfile + metadata: + labels: + spo.x-k8s.io/container-id: bpf-recorder + name: bpfrecorder-apparmor + namespace: security-profiles-operator + spec: + abstract: + capability: + allowedCapabilities: + - bpf + - chown + - dac_override + - dac_read_search + - mknod + - perfmon + - setgid + - setuid + - sys_admin + - sys_resource + executable: + allowedExecutables: + - /security-profiles-operator + filesystem: + readOnlyPaths: + - / + - /proc/@{pid}/cgroup + - /proc/@{pid}/fd/ + - /proc/@{pid}/maps + - /proc/@{pid}/mountinfo + - /proc/@{pid}/setgroups + - /proc/@{pid}/status + - /proc/@{pid}/uid_map + - /proc/filesystems + - /proc/sys/kernel/cap_last_cap + - /proc/sys/net/core/somaxconn + - /sys/devices/kprobe/type + - /sys/devices/system/cpu/online + - /sys/fs/bpf/ + - /sys/kernel/btf/vmlinux + - /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/id + - /sys/kernel/debug/tracing/events/sched/sched_process_exec/id + - /sys/kernel/debug/tracing/events/sched/sched_process_exit/id + - /sys/kernel/debug/tracing/events/syscalls/sys_enter_socket/id + - /sys/kernel/mm/transparent_hugepage/hpage_pmd_size + - /sys/kernel/security/lsm + readWritePaths: + - /dev/null + network: + allowedProtocols: + allowTcp: true + allowUdp: true + disabled: false security-profiles-operator.json: | { "defaultAction": "SCMP_ACT_ERRNO", @@ -3251,6 +3306,55 @@ data: (blockinherit container) (typepermissive process) ) + spo-apparmor.yaml: |+ + apiVersion: security-profiles-operator.x-k8s.io/v1alpha1 + kind: AppArmorProfile + metadata: + labels: + spo.x-k8s.io/container-id: security-profiles-operator + name: spo-apprmor + namespace: security-profiles-operator + spec: + abstract: + capability: + allowedCapabilities: + - chown + - dac_override + - dac_read_search + - fowner + - fsetid + - mknod + - setgid + - setpcap + - setuid + - sys_admin + executable: + allowedExecutables: + - /security-profiles-operator + filesystem: + readOnlyPaths: + - / + - /opt/spo-profiles/** + - /proc/@{pid}/fd/ + - /proc/@{pid}/maps + - /proc/@{pid}/mountinfo + - /proc/@{pid}/setgroups + - /proc/@{pid}/status + - /proc/@{pid}/uid_map + - /proc/filesystems + - /proc/sys/kernel/cap_last_cap + - /proc/sys/net/core/somaxconn + - /sys/kernel/mm/transparent_hugepage/hpage_pmd_size + readWritePaths: + - /dev/null + - /host/var/lib/kubelet/seccomp/** + - /var/lib/security-profiles-operator/kubelet-config.json + network: + allowedProtocols: + allowTcp: true + allowUdp: true + disabled: false + kind: ConfigMap metadata: labels: diff --git a/deploy/operator.yaml b/deploy/operator.yaml index 61b9132660..52dc7191dc 100644 --- a/deploy/operator.yaml +++ b/deploy/operator.yaml @@ -3072,6 +3072,61 @@ data: } ] } + bpfrecorder-apparmor.yaml: | + apiVersion: security-profiles-operator.x-k8s.io/v1alpha1 + kind: AppArmorProfile + metadata: + labels: + spo.x-k8s.io/container-id: bpf-recorder + name: bpfrecorder-apparmor + namespace: security-profiles-operator + spec: + abstract: + capability: + allowedCapabilities: + - bpf + - chown + - dac_override + - dac_read_search + - mknod + - perfmon + - setgid + - setuid + - sys_admin + - sys_resource + executable: + allowedExecutables: + - /security-profiles-operator + filesystem: + readOnlyPaths: + - / + - /proc/@{pid}/cgroup + - /proc/@{pid}/fd/ + - /proc/@{pid}/maps + - /proc/@{pid}/mountinfo + - /proc/@{pid}/setgroups + - /proc/@{pid}/status + - /proc/@{pid}/uid_map + - /proc/filesystems + - /proc/sys/kernel/cap_last_cap + - /proc/sys/net/core/somaxconn + - /sys/devices/kprobe/type + - /sys/devices/system/cpu/online + - /sys/fs/bpf/ + - /sys/kernel/btf/vmlinux + - /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/id + - /sys/kernel/debug/tracing/events/sched/sched_process_exec/id + - /sys/kernel/debug/tracing/events/sched/sched_process_exit/id + - /sys/kernel/debug/tracing/events/syscalls/sys_enter_socket/id + - /sys/kernel/mm/transparent_hugepage/hpage_pmd_size + - /sys/kernel/security/lsm + readWritePaths: + - /dev/null + network: + allowedProtocols: + allowTcp: true + allowUdp: true + disabled: false security-profiles-operator.json: | { "defaultAction": "SCMP_ACT_ERRNO", @@ -3238,6 +3293,55 @@ data: (blockinherit container) (typepermissive process) ) + spo-apparmor.yaml: |+ + apiVersion: security-profiles-operator.x-k8s.io/v1alpha1 + kind: AppArmorProfile + metadata: + labels: + spo.x-k8s.io/container-id: security-profiles-operator + name: spo-apprmor + namespace: security-profiles-operator + spec: + abstract: + capability: + allowedCapabilities: + - chown + - dac_override + - dac_read_search + - fowner + - fsetid + - mknod + - setgid + - setpcap + - setuid + - sys_admin + executable: + allowedExecutables: + - /security-profiles-operator + filesystem: + readOnlyPaths: + - / + - /opt/spo-profiles/** + - /proc/@{pid}/fd/ + - /proc/@{pid}/maps + - /proc/@{pid}/mountinfo + - /proc/@{pid}/setgroups + - /proc/@{pid}/status + - /proc/@{pid}/uid_map + - /proc/filesystems + - /proc/sys/kernel/cap_last_cap + - /proc/sys/net/core/somaxconn + - /sys/kernel/mm/transparent_hugepage/hpage_pmd_size + readWritePaths: + - /dev/null + - /host/var/lib/kubelet/seccomp/** + - /var/lib/security-profiles-operator/kubelet-config.json + network: + allowedProtocols: + allowTcp: true + allowUdp: true + disabled: false + kind: ConfigMap metadata: labels: diff --git a/deploy/webhook-operator.yaml b/deploy/webhook-operator.yaml index 72e9662392..6b5fd31b97 100644 --- a/deploy/webhook-operator.yaml +++ b/deploy/webhook-operator.yaml @@ -3043,6 +3043,61 @@ data: } ] } + bpfrecorder-apparmor.yaml: | + apiVersion: security-profiles-operator.x-k8s.io/v1alpha1 + kind: AppArmorProfile + metadata: + labels: + spo.x-k8s.io/container-id: bpf-recorder + name: bpfrecorder-apparmor + namespace: security-profiles-operator + spec: + abstract: + capability: + allowedCapabilities: + - bpf + - chown + - dac_override + - dac_read_search + - mknod + - perfmon + - setgid + - setuid + - sys_admin + - sys_resource + executable: + allowedExecutables: + - /security-profiles-operator + filesystem: + readOnlyPaths: + - / + - /proc/@{pid}/cgroup + - /proc/@{pid}/fd/ + - /proc/@{pid}/maps + - /proc/@{pid}/mountinfo + - /proc/@{pid}/setgroups + - /proc/@{pid}/status + - /proc/@{pid}/uid_map + - /proc/filesystems + - /proc/sys/kernel/cap_last_cap + - /proc/sys/net/core/somaxconn + - /sys/devices/kprobe/type + - /sys/devices/system/cpu/online + - /sys/fs/bpf/ + - /sys/kernel/btf/vmlinux + - /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/id + - /sys/kernel/debug/tracing/events/sched/sched_process_exec/id + - /sys/kernel/debug/tracing/events/sched/sched_process_exit/id + - /sys/kernel/debug/tracing/events/syscalls/sys_enter_socket/id + - /sys/kernel/mm/transparent_hugepage/hpage_pmd_size + - /sys/kernel/security/lsm + readWritePaths: + - /dev/null + network: + allowedProtocols: + allowTcp: true + allowUdp: true + disabled: false security-profiles-operator.json: | { "defaultAction": "SCMP_ACT_ERRNO", @@ -3209,6 +3264,55 @@ data: (blockinherit container) (typepermissive process) ) + spo-apparmor.yaml: |+ + apiVersion: security-profiles-operator.x-k8s.io/v1alpha1 + kind: AppArmorProfile + metadata: + labels: + spo.x-k8s.io/container-id: security-profiles-operator + name: spo-apprmor + namespace: security-profiles-operator + spec: + abstract: + capability: + allowedCapabilities: + - chown + - dac_override + - dac_read_search + - fowner + - fsetid + - mknod + - setgid + - setpcap + - setuid + - sys_admin + executable: + allowedExecutables: + - /security-profiles-operator + filesystem: + readOnlyPaths: + - / + - /opt/spo-profiles/** + - /proc/@{pid}/fd/ + - /proc/@{pid}/maps + - /proc/@{pid}/mountinfo + - /proc/@{pid}/setgroups + - /proc/@{pid}/status + - /proc/@{pid}/uid_map + - /proc/filesystems + - /proc/sys/kernel/cap_last_cap + - /proc/sys/net/core/somaxconn + - /sys/kernel/mm/transparent_hugepage/hpage_pmd_size + readWritePaths: + - /dev/null + - /host/var/lib/kubelet/seccomp/** + - /var/lib/security-profiles-operator/kubelet-config.json + network: + allowedProtocols: + allowTcp: true + allowUdp: true + disabled: false + kind: ConfigMap metadata: labels: diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go index 7ff2172521..5ec186948d 100644 --- a/internal/pkg/config/config.go +++ b/internal/pkg/config/config.go @@ -105,6 +105,10 @@ const ( // KubeletDirEnvKey is the environment variable key for custom kubelet directory. KubeletDirEnvKey = "KUBELET_DIR" + // AppArmorEnvKey is the environment variable key for enabling apaprmor profiles for + // security-profiles-operator itself. + AppArmorEnvKey = "SPO_APPARMOR" + // DefaultProfilingPort is the start port where the profiling endpoint runs. DefaultProfilingPort = 6060 @@ -167,6 +171,22 @@ const ( // OCIProfilePrefix is the prefix used for specifying security profiles // from OCI artifacts. OCIProfilePrefix = "oci://" + + // SpoApparmorProfile is the default file name for apparmor profile used by + // the security-profiles-operator container part of spod daemonset. + SpoApparmorProfile = "spo-apparmor.yaml" + + // SpoApparmorProfileName is the name of the apparmor profile used by the + // security-profiles-operator container part of spod daemonset. + SpoApparmorProfileName = "spo-apparmor" + + // BpfRecorderApparmorProfile is the default file name for apparmor profile used + // by the bpf-recorder container part of spod daemonset. + BpfRecorderApparmorProfile = "bpfrecorder-apparmor.yaml" + + // BpfRecorderApparmorProfileName is the name of the apparmor profile used by + // the bpf-recorder container part of spod dameonset. + BpfRecorderApparmorProfileName = "bpfrecorder-apparmor" ) // ProfileRecordingOutputPath is the path where the recorded profiles will be diff --git a/internal/pkg/manager/spod/spod_controller.go b/internal/pkg/manager/spod/spod_controller.go index 4d2603e851..5b576b6911 100644 --- a/internal/pkg/manager/spod/spod_controller.go +++ b/internal/pkg/manager/spod/spod_controller.go @@ -570,6 +570,15 @@ func (r *ReconcileSPOd) getConfiguredSPOd( templateSpec.Containers = append(templateSpec.Containers, ctr) // pass the bpf recorder env var to the daemon as the profile recorder is otherwise disabled addEnvVar(templateSpec, config.EnableBpfRecorderEnvKey) + + // Configure the apparmor profile for bpf-recorder when apparmor is enabled. + if cfg.Spec.EnableAppArmor { + localApparmorProfile := config.BpfRecorderApparmorProfileName + ctr.SecurityContext.AppArmorProfile = &corev1.AppArmorProfile{ + Type: corev1.AppArmorProfileTypeLocalhost, + LocalhostProfile: &localApparmorProfile, + } + } } // AppArmor parameters @@ -585,6 +594,12 @@ func (r *ReconcileSPOd) getConfiguredSPOd( sc.RunAsUser = &userRoot sc.RunAsGroup = &userRoot + localSpoApparmorProfile := config.SpoApparmorProfileName + sc.AppArmorProfile = &corev1.AppArmorProfile{ + Type: corev1.AppArmorProfileTypeLocalhost, + LocalhostProfile: &localSpoApparmorProfile, + } + templateSpec.Containers[bindata.ContainerIDDaemon].Args = append( templateSpec.Containers[bindata.ContainerIDDaemon].Args, "--with-apparmor=true") @@ -595,7 +610,20 @@ func (r *ReconcileSPOd) getConfiguredSPOd( } newSPOd.ObjectMeta.Annotations[appArmorAnnotation] = "unconfined" - // HostPID is required for AppArmor when trying to get access to the host ns + // A more privileged init container is required when apparmor is enabled, in order + // to install the apparmor profile for spo itself. + templateSpec.InitContainers[bindata.InitContainerIDNonRootenabler].Args = append( + templateSpec.InitContainers[bindata.InitContainerIDNonRootenabler].Args, + "--apparmor=true") + isc := templateSpec.InitContainers[bindata.InitContainerIDNonRootenabler].SecurityContext + isc.AllowPrivilegeEscalation = &truly + isc.Privileged = &truly + isc.ReadOnlyRootFilesystem = &falsely + isc.RunAsUser = &userRoot + isc.RunAsGroup = &userRoot + + // HostPID is required for AppArmor in order to get access to the host ns + // when installing the Apparmor profiles. templateSpec.HostPID = true } diff --git a/internal/pkg/nonrootenabler/nonrootenabler.go b/internal/pkg/nonrootenabler/nonrootenabler.go index ad44e64860..26ee45b143 100644 --- a/internal/pkg/nonrootenabler/nonrootenabler.go +++ b/internal/pkg/nonrootenabler/nonrootenabler.go @@ -18,6 +18,7 @@ package nonrootenabler import ( "encoding/json" + "errors" "fmt" "os" "path" @@ -25,7 +26,10 @@ import ( "github.com/go-logr/logr" "sigs.k8s.io/release-utils/util" + "sigs.k8s.io/security-profiles-operator/api/apparmorprofile/v1alpha1" + "sigs.k8s.io/security-profiles-operator/internal/pkg/artifact" "sigs.k8s.io/security-profiles-operator/internal/pkg/config" + "sigs.k8s.io/security-profiles-operator/internal/pkg/daemon/apparmorprofile" ) // NonRootEnabler is the main type of this package. @@ -45,7 +49,7 @@ func (n *NonRootEnabler) SetImpl(i impl) { } // Run executes the NonRootEnabler and returns an error if anything fails. -func (n *NonRootEnabler) Run(logger logr.Logger, runtime, kubeletDir string) error { +func (n *NonRootEnabler) Run(logger logr.Logger, runtime, kubeletDir string, apparmor bool) error { const dirPermissions os.FileMode = 0o744 const filePermissions os.FileMode = 0o644 @@ -122,6 +126,17 @@ func (n *NonRootEnabler) Run(logger logr.Logger, runtime, kubeletDir string) err return fmt.Errorf("copy local security profiles: %w", err) } + aaManager := apparmorprofile.NewAppArmorProfileManager(logger) + if apparmor && aaManager.Enabled() { + for _, p := range []string{config.SpoApparmorProfile, config.BpfRecorderApparmorProfile} { + profile := path.Join(config.DefaultSpoProfilePath, p) + logger.Info("Installing apparmor profile: " + profile) + if err := n.impl.InstallApparmor(aaManager, profile); err != nil { + return fmt.Errorf("installing apparmor profile: %w", err) + } + } + } + return nil } @@ -135,6 +150,7 @@ type impl interface { Chown(name string, uid, gid int) error CopyDirContentsLocal(src, dst string) error SaveKubeletConfig(filename string, kubeletConfig []byte, perm os.FileMode) error + InstallApparmor(manager apparmorprofile.ProfileManager, filename string) error } type defaultImpl struct{} @@ -166,3 +182,22 @@ func (*defaultImpl) CopyDirContentsLocal(src, dst string) error { func (*defaultImpl) SaveKubeletConfig(filename string, kubeletConfig []byte, perm os.FileMode) error { return os.WriteFile(filename, kubeletConfig, perm) } + +func (*defaultImpl) InstallApparmor(manager apparmorprofile.ProfileManager, filename string) error { + content, err := os.ReadFile(filename) + if err != nil { + return fmt.Errorf("reading apparmor profile content: %w", err) + } + profile, err := artifact.ReadProfile(content) + if err != nil { + return fmt.Errorf("parsing apparmor profile: %w", err) + } + ap, ok := profile.(*v1alpha1.AppArmorProfile) + if !ok { + return errors.New("failed converting apparmor profile") + } + if _, err := manager.InstallProfile(ap); err != nil { + return fmt.Errorf("installing apparmor profile: %w", err) + } + return nil +} diff --git a/internal/pkg/nonrootenabler/nonrootenabler_test.go b/internal/pkg/nonrootenabler/nonrootenabler_test.go index 386a795e64..c01f2b7ca0 100644 --- a/internal/pkg/nonrootenabler/nonrootenabler_test.go +++ b/internal/pkg/nonrootenabler/nonrootenabler_test.go @@ -102,7 +102,7 @@ func TestRun(t *testing.T) { tc.prepare(mock) sut.SetImpl(mock) - err := sut.Run(logr.Discard(), "", config.KubeletDir()) + err := sut.Run(logr.Discard(), "", config.KubeletDir(), false) if tc.shouldError { require.Error(t, err) } else { diff --git a/internal/pkg/nonrootenabler/nonrootenablerfakes/fake_impl.go b/internal/pkg/nonrootenabler/nonrootenablerfakes/fake_impl.go index 21a7fa8f17..6db2b6a800 100644 --- a/internal/pkg/nonrootenabler/nonrootenablerfakes/fake_impl.go +++ b/internal/pkg/nonrootenabler/nonrootenablerfakes/fake_impl.go @@ -20,6 +20,8 @@ package nonrootenablerfakes import ( "os" "sync" + + "sigs.k8s.io/security-profiles-operator/internal/pkg/daemon/apparmorprofile" ) type FakeImpl struct { @@ -60,6 +62,18 @@ type FakeImpl struct { copyDirContentsLocalReturnsOnCall map[int]struct { result1 error } + InstallApparmorStub func(apparmorprofile.ProfileManager, string) error + installApparmorMutex sync.RWMutex + installApparmorArgsForCall []struct { + arg1 apparmorprofile.ProfileManager + arg2 string + } + installApparmorReturns struct { + result1 error + } + installApparmorReturnsOnCall map[int]struct { + result1 error + } MkdirAllStub func(string, os.FileMode) error mkdirAllMutex sync.RWMutex mkdirAllArgsForCall []struct { @@ -301,6 +315,68 @@ func (fake *FakeImpl) CopyDirContentsLocalReturnsOnCall(i int, result1 error) { }{result1} } +func (fake *FakeImpl) InstallApparmor(arg1 apparmorprofile.ProfileManager, arg2 string) error { + fake.installApparmorMutex.Lock() + ret, specificReturn := fake.installApparmorReturnsOnCall[len(fake.installApparmorArgsForCall)] + fake.installApparmorArgsForCall = append(fake.installApparmorArgsForCall, struct { + arg1 apparmorprofile.ProfileManager + arg2 string + }{arg1, arg2}) + stub := fake.InstallApparmorStub + fakeReturns := fake.installApparmorReturns + fake.recordInvocation("InstallApparmor", []interface{}{arg1, arg2}) + fake.installApparmorMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeImpl) InstallApparmorCallCount() int { + fake.installApparmorMutex.RLock() + defer fake.installApparmorMutex.RUnlock() + return len(fake.installApparmorArgsForCall) +} + +func (fake *FakeImpl) InstallApparmorCalls(stub func(apparmorprofile.ProfileManager, string) error) { + fake.installApparmorMutex.Lock() + defer fake.installApparmorMutex.Unlock() + fake.InstallApparmorStub = stub +} + +func (fake *FakeImpl) InstallApparmorArgsForCall(i int) (apparmorprofile.ProfileManager, string) { + fake.installApparmorMutex.RLock() + defer fake.installApparmorMutex.RUnlock() + argsForCall := fake.installApparmorArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeImpl) InstallApparmorReturns(result1 error) { + fake.installApparmorMutex.Lock() + defer fake.installApparmorMutex.Unlock() + fake.InstallApparmorStub = nil + fake.installApparmorReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeImpl) InstallApparmorReturnsOnCall(i int, result1 error) { + fake.installApparmorMutex.Lock() + defer fake.installApparmorMutex.Unlock() + fake.InstallApparmorStub = nil + if fake.installApparmorReturnsOnCall == nil { + fake.installApparmorReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.installApparmorReturnsOnCall[i] = struct { + result1 error + }{result1} +} + func (fake *FakeImpl) MkdirAll(arg1 string, arg2 os.FileMode) error { fake.mkdirAllMutex.Lock() ret, specificReturn := fake.mkdirAllReturnsOnCall[len(fake.mkdirAllArgsForCall)] @@ -566,6 +642,8 @@ func (fake *FakeImpl) Invocations() map[string][][]interface{} { defer fake.chownMutex.RUnlock() fake.copyDirContentsLocalMutex.RLock() defer fake.copyDirContentsLocalMutex.RUnlock() + fake.installApparmorMutex.RLock() + defer fake.installApparmorMutex.RUnlock() fake.mkdirAllMutex.RLock() defer fake.mkdirAllMutex.RUnlock() fake.saveKubeletConfigMutex.RLock()