diff --git a/spo-seccomp-mitigation/README.md b/spo-seccomp-mitigation/README.md new file mode 100644 index 0000000..1f49802 --- /dev/null +++ b/spo-seccomp-mitigation/README.md @@ -0,0 +1,30 @@ +# SPO Seccomp Mitigation for CVE-2026-31431 (Copy Fail) + +This directory provides a mitigation for CVE-2026-31431 using the Kubernetes Security Profiles Operator (SPO). It uses a custom `SeccompProfile` that copies containerd's default allowed syscalls but blocks both `AF_VSOCK` and `AF_ALG` (socket family 38). + +## Instructions + +**1. Install the Security Profiles Operator (SPO)** +If you do not already have SPO installed in your cluster, follow the installation instructions here: +[SPO Installation & Usage](https://github.com/kubernetes-sigs/security-profiles-operator/blob/main/installation-usage.md) + +**2. Deploy the Mitigated Seccomp Profile** +Apply the `seccomp-profile.yaml` to create the profile in your namespace. This profile explicitly denies `AF_ALG` socket creation. +```bash +kubectl apply -f seccomp-profile.yaml +``` + +**3. Enable Binding on the Namespace** +For the binding to take effect, you must label the target namespace to permit the Security Profiles Operator to modify pods within it: +```bash +kubectl label ns my-namespace spo.x-k8s.io/enable-binding=true +``` + +**4. Bind the Profile to Containers** +Apply the `profile-binding.yaml` to bind the new seccomp profile to all containers in the namespace. +```bash +kubectl apply -f profile-binding.yaml +``` + +**5. Restart Existing Pods** +The binding is applied via a mutating webhook during pod creation. Existing pods must be restarted or recreated to pick up the new profile and be protected. diff --git a/spo-seccomp-mitigation/profile-binding.yaml b/spo-seccomp-mitigation/profile-binding.yaml new file mode 100644 index 0000000..d4ea8cd --- /dev/null +++ b/spo-seccomp-mitigation/profile-binding.yaml @@ -0,0 +1,10 @@ +apiVersion: security-profiles-operator.x-k8s.io/v1alpha1 +kind: ProfileBinding +metadata: + name: mitigate-copyfail-binding + namespace: my-namespace +spec: + profileRef: + kind: SeccompProfile + name: containerd-default-copyfail-mitigated + image: "*" # Apply to all images in a namespace diff --git a/spo-seccomp-mitigation/seccomp-profile.yaml b/spo-seccomp-mitigation/seccomp-profile.yaml new file mode 100644 index 0000000..436344d --- /dev/null +++ b/spo-seccomp-mitigation/seccomp-profile.yaml @@ -0,0 +1,107 @@ +apiVersion: security-profiles-operator.x-k8s.io/v1beta1 +kind: SeccompProfile +metadata: + name: containerd-default-copyfail-mitigated + namespace: my-namespace +spec: + defaultAction: SCMP_ACT_ERRNO + architectures: + - SCMP_ARCH_X86_64 + - SCMP_ARCH_X86 + - SCMP_ARCH_X32 + - SCMP_ARCH_AARCH64 + - SCMP_ARCH_ARM + syscalls: + # 1. The Mitigated Socket Rule + # Replaces containerd's standard rule to block BOTH AF_VSOCK (40) and AF_ALG (38) + - names: + - socket + action: SCMP_ACT_ALLOW + args: + - index: 0 + value: 40 # Containerd default: block AF_VSOCK + op: SCMP_CMP_NEQ + - index: 0 + value: 38 # Mitigation: block AF_ALG + op: SCMP_CMP_NEQ + + # 2. Containerd's conditional allowed personality domains + - names: ["personality"] + action: SCMP_ACT_ALLOW + args: [{ index: 0, value: 0, op: SCMP_CMP_EQ }] + - names: ["personality"] + action: SCMP_ACT_ALLOW + args: [{ index: 0, value: 8, op: SCMP_CMP_EQ }] + - names: ["personality"] + action: SCMP_ACT_ALLOW + args: [{ index: 0, value: 131072, op: SCMP_CMP_EQ }] # 0x20000 + - names: ["personality"] + action: SCMP_ACT_ALLOW + args: [{ index: 0, value: 131080, op: SCMP_CMP_EQ }] # 0x20008 + - names: ["personality"] + action: SCMP_ACT_ALLOW + args: [{ index: 0, value: 4294967295, op: SCMP_CMP_EQ }] # 0xffffffff + + # 3. Containerd's unconditionally allowed baseline syscalls + - action: SCMP_ACT_ALLOW + names: [ + "accept", "accept4", "access", "adjtimex", "alarm", "bind", "brk", "cachestat", + "capget", "capset", "chdir", "chmod", "chown", "chown32", "clock_adjtime", + "clock_adjtime64", "clock_getres", "clock_getres_time64", "clock_gettime", + "clock_gettime64", "clock_nanosleep", "clock_nanosleep_time64", "close", + "close_range", "connect", "copy_file_range", "creat", "dup", "dup2", "dup3", + "epoll_create", "epoll_create1", "epoll_ctl", "epoll_ctl_old", "epoll_pwait", + "epoll_pwait2", "epoll_wait", "epoll_wait_old", "eventfd", "eventfd2", "execve", + "execveat", "exit", "exit_group", "faccessat", "faccessat2", "fadvise64", + "fadvise64_64", "fallocate", "fanotify_mark", "fchdir", "fchmod", "fchmodat", + "fchmodat2", "fchown", "fchown32", "fchownat", "fcntl", "fcntl64", "fdatasync", + "fgetxattr", "flistxattr", "flock", "fork", "fremovexattr", "fsetxattr", "fstat", + "fstat64", "fstatat64", "fstatfs", "fstatfs64", "fsync", "ftruncate", "ftruncate64", + "futex", "futex_requeue", "futex_time64", "futex_wait", "futex_waitv", "futex_wake", + "futimesat", "getcpu", "getcwd", "getdents", "getdents64", "getegid", "getegid32", + "geteuid", "geteuid32", "getgid", "getgid32", "getgroups", "getgroups32", "getitimer", + "getpeername", "getpgid", "getpgrp", "getpid", "getppid", "getpriority", "getrandom", + "getresgid", "getresgid32", "getresuid", "getresuid32", "getrlimit", "get_robust_list", + "getrusage", "getsid", "getsockname", "getsockopt", "get_thread_area", "gettid", + "gettimeofday", "getuid", "getuid32", "getxattr", "getxattrat", "inotify_add_watch", + "inotify_init", "inotify_init1", "inotify_rm_watch", "io_cancel", "ioctl", "io_destroy", + "io_getevents", "io_pgetevents", "io_pgetevents_time64", "ioprio_get", "ioprio_set", + "io_setup", "io_submit", "ipc", "kill", "landlock_add_rule", "landlock_create_ruleset", + "landlock_restrict_self", "lchown", "lchown32", "lgetxattr", "link", "linkat", "listen", + "listmount", "listxattr", "listxattrat", "llistxattr", "_llseek", "lremovexattr", "lseek", + "lsetxattr", "lsm_get_self_attr", "lsm_list_modules", "lsm_set_self_attr", "lstat", + "lstat64", "madvise", "membarrier", "memfd_create", "memfd_secret", "mincore", "mkdir", + "mkdirat", "mknod", "mknodat", "mlock", "mlock2", "mlockall", "map_shadow_stack", "mmap", + "mmap2", "mprotect", "mq_getsetattr", "mq_notify", "mq_open", "mq_timedreceive", + "mq_timedreceive_time64", "mq_timedsend", "mq_timedsend_time64", "mq_unlink", "mremap", + "mseal", "msgctl", "msgget", "msgrcv", "msgsnd", "msync", "munlock", "munlockall", + "munmap", "name_to_handle_at", "nanosleep", "newfstatat", "_newselect", "open", "openat", + "openat2", "pause", "pidfd_open", "pidfd_send_signal", "pipe", "pipe2", "pkey_alloc", + "pkey_free", "pkey_mprotect", "poll", "ppoll", "ppoll_time64", "prctl", "pread64", + "preadv", "preadv2", "prlimit64", "process_mrelease", "process_vm_readv", "process_vm_writev", + "ptrace", "pselect6", "pselect6_time64", "pwrite64", "pwritev", "pwritev2", "read", + "readahead", "readlink", "readlinkat", "readv", "recv", "recvfrom", "recvmmsg", + "recvmmsg_time64", "recvmsg", "remap_file_pages", "removexattr", "removexattrat", + "rename", "renameat", "renameat2", "restart_syscall", "rmdir", "rseq", "rt_sigaction", + "rt_sigpending", "rt_sigprocmask", "rt_sigqueueinfo", "rt_sigreturn", "rt_sigsuspend", + "rt_sigtimedwait", "rt_sigtimedwait_time64", "rt_tgsigqueueinfo", "sched_getaffinity", + "sched_getattr", "sched_getparam", "sched_get_priority_max", "sched_get_priority_min", + "sched_getscheduler", "sched_rr_get_interval", "sched_rr_get_interval_time64", + "sched_setaffinity", "sched_setattr", "sched_setparam", "sched_setscheduler", "sched_yield", + "seccomp", "select", "semctl", "semget", "semop", "semtimedop", "semtimedop_time64", + "send", "sendfile", "sendfile64", "sendmmsg", "sendmsg", "sendto", "setfsgid", + "setfsgid32", "setfsuid", "setfsuid32", "setgid", "setgid32", "setgroups", "setgroups32", + "setitimer", "setpgid", "setpriority", "setregid", "setregid32", "setresgid", "setresgid32", + "setresuid", "setresuid32", "setreuid", "setreuid32", "setrlimit", "set_robust_list", + "setsid", "setsockopt", "set_thread_area", "set_tid_address", "setuid", "setuid32", + "setxattr", "setxattrat", "shmat", "shmctl", "shmdt", "shmget", "shutdown", "sigaltstack", + "signalfd", "signalfd4", "sigprocmask", "sigreturn", "socketcall", "socketpair", "splice", + "stat", "stat64", "statfs", "statfs64", "statmount", "statx", "symlink", "symlinkat", + "sync", "sync_file_range", "syncfs", "sysinfo", "tee", "tgkill", "time", "timer_create", + "timer_delete", "timer_getoverrun", "timer_gettime", "timer_gettime64", "timer_settime", + "timer_settime64", "timerfd_create", "timerfd_gettime", "timerfd_gettime64", "timerfd_settime", + "timerfd_settime64", "times", "tkill", "truncate", "truncate64", "ugetrlimit", "umask", + "uname", "unlink", "unlinkat", "uretprobe", "utime", "utimensat", "utimensat_time64", + "utimes", "vfork", "vmsplice", "wait4", "waitid", "waitpid", "write", "writev" + ] +