Last active
August 31, 2022 23:51
-
-
Save chrisdlangton/f94fd36028a0ba46b73e6629524d5710 to your computer and use it in GitHub Desktop.
Automatically generate seccomp profile json by learning from container activity using sysdig
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import fileinput | |
import json | |
import argparse | |
SECCOMP_PROFILE = ('{"defaultAction": "SCMP_ACT_ERRNO",' | |
'"architectures": [' | |
'"SCMP_ARCH_X86_64",' | |
'"SCMP_ARCH_X86",' | |
'"SCMP_ARCH_X32"],' | |
'"syscalls": []}') | |
def main(): | |
parser = argparse.ArgumentParser( | |
description="Search for syscalls stored inside of an strace log and convert it to a Docker seccomp JSON profile" ) | |
parser.add_argument("strace", | |
help="Path to file containing the output from running strace on an application") | |
#parser.add_argument("-o", help="Optional output path") | |
parser.add_argument("-s", dest="sysdig", action="store_true", help="For handling sysdig output instead of strace") | |
args = parser.parse_args() | |
app_syscalls = set() | |
with fileinput.input(files=(args.strace)) as f: | |
if args.sysdig: | |
a = [] | |
for x in f: | |
line = x.replace('>','<').split(' < ', 1) | |
if not len(line) > 1: | |
continue | |
a.append(line[1].split(' ')[0]) | |
app_syscalls.update(a) | |
else: | |
app_syscalls.update((x.split('(', 1)[0] for x in f if x[0].isalpha())) | |
app_syscalls.intersection_update(SYSCALLS) # validate syscalls actually exist | |
to_profile(app_syscalls) | |
def _load_template(): | |
template = json.loads(SECCOMP_PROFILE) | |
return template | |
def _syscall_template(): | |
return json.loads('{"name": "","action": "SCMP_ACT_ALLOW","args": []}') | |
def to_profile(syscalls): | |
template = _load_template() | |
syscalls.update(BASE_SYSCALLS) | |
newsyscall = _syscall_template() | |
newsyscall["names"] = list(syscalls) | |
template["syscalls"].append(newsyscall) | |
print(json.dumps(template, indent=4)) | |
SYSCALLS = ( | |
"read", | |
"write", | |
"open", | |
"close", | |
"stat", | |
"fstat", | |
"lstat", | |
"poll", | |
"lseek", | |
"mmap", | |
"mprotect", | |
"munmap", | |
"brk", | |
"rt_sigaction", | |
"rt_sigprocmask", | |
"rt_sigreturn", | |
"ioctl", | |
"pread64", | |
"pwrite64", | |
"readv", | |
"writev", | |
"access", | |
"pipe", | |
"select", | |
"sched_yield", | |
"mremap", | |
"msync", | |
"mincore", | |
"madvise", | |
"shmget", | |
"shmat", | |
"shmctl", | |
"dup", | |
"dup2", | |
"pause", | |
"nanosleep", | |
"getitimer", | |
"alarm", | |
"setitimer", | |
"getpid", | |
"sendfile", | |
"socket", | |
"connect", | |
"accept", | |
"sendto", | |
"recvfrom", | |
"sendmsg", | |
"recvmsg", | |
"shutdown", | |
"bind", | |
"listen", | |
"getsockname", | |
"getpeername", | |
"socketpair", | |
"setsockopt", | |
"getsockopt", | |
"clone", | |
"fork", | |
"vfork", | |
"execve", | |
"exit", | |
"wait4", | |
"kill", | |
"uname", | |
"semget", | |
"semop", | |
"semctl", | |
"shmdt", | |
"msgget", | |
"msgsnd", | |
"msgrcv", | |
"msgctl", | |
"fcntl", | |
"flock", | |
"fsync", | |
"fdatasync", | |
"truncate", | |
"ftruncate", | |
"getdents", | |
"getcwd", | |
"chdir", | |
"fchdir", | |
"rename", | |
"mkdir", | |
"rmdir", | |
"creat", | |
"link", | |
"unlink", | |
"symlink", | |
"readlink", | |
"chmod", | |
"fchmod", | |
"chown", | |
"fchown", | |
"lchown", | |
"umask", | |
"gettimeofday", | |
"getrlimit", | |
"getrusage", | |
"sysinfo", | |
"times", | |
"ptrace", | |
"getuid", | |
"syslog", | |
"getgid", | |
"setuid", | |
"setgid", | |
"geteuid", | |
"getegid", | |
"setpgid", | |
"getppid", | |
"getpgrp", | |
"setsid", | |
"setreuid", | |
"setregid", | |
"getgroups", | |
"setgroups", | |
"setresuid", | |
"getresuid", | |
"setresgid", | |
"getresgid", | |
"getpgid", | |
"setfsuid", | |
"setfsgid", | |
"getsid", | |
"capget", | |
"capset", | |
"rt_sigpending", | |
"rt_sigtimedwait", | |
"rt_sigqueueinfo", | |
"rt_sigsuspend", | |
"sigaltstack", | |
"utime", | |
"mknod", | |
"uselib", | |
"personality", | |
"ustat", | |
"statfs", | |
"fstatfs", | |
"sysfs", | |
"getpriority", | |
"setpriority", | |
"sched_setparam", | |
"sched_getparam", | |
"sched_setscheduler", | |
"sched_getscheduler", | |
"sched_get_priority_max", | |
"sched_get_priority_min", | |
"sched_rr_get_interval", | |
"mlock", | |
"munlock", | |
"mlockall", | |
"munlockall", | |
"vhangup", | |
"modify_ldt", | |
"pivot_root", | |
"_sysctl", | |
"prctl", | |
"arch_prctl", | |
"adjtimex", | |
"setrlimit", | |
"chroot", | |
"sync", | |
"acct", | |
"settimeofday", | |
"mount", | |
"umount2", | |
"swapon", | |
"swapoff", | |
"reboot", | |
"sethostname", | |
"setdomainname", | |
"iopl", | |
"ioperm", | |
"create_module", | |
"init_module", | |
"delete_module", | |
"get_kernel_syms", | |
"query_module", | |
"quotactl", | |
"nfsservctl", | |
"getpmsg", | |
"putpmsg", | |
"afs_syscall", | |
"tuxcall", | |
"security", | |
"gettid", | |
"readahead", | |
"setxattr", | |
"lsetxattr", | |
"fsetxattr", | |
"getxattr", | |
"lgetxattr", | |
"fgetxattr", | |
"listxattr", | |
"llistxattr", | |
"flistxattr", | |
"removexattr", | |
"lremovexattr", | |
"fremovexattr", | |
"tkill", | |
"time", | |
"futex", | |
"sched_setaffinity", | |
"sched_getaffinity", | |
"set_thread_area", | |
"io_setup", | |
"io_destroy", | |
"io_getevents", | |
"io_submit", | |
"io_cancel", | |
"get_thread_area", | |
"lookup_dcookie", | |
"epoll_create", | |
"epoll_ctl_old", | |
"epoll_wait_old", | |
"remap_file_pages", | |
"getdents64", | |
"set_tid_address", | |
"restart_syscall", | |
"semtimedop", | |
"fadvise64", | |
"timer_create", | |
"timer_settime", | |
"timer_gettime", | |
"timer_getoverrun", | |
"timer_delete", | |
"clock_settime", | |
"clock_gettime", | |
"clock_getres", | |
"clock_nanosleep", | |
"exit_group", | |
"epoll_wait", | |
"epoll_ctl", | |
"tgkill", | |
"utimes", | |
"vserver", | |
"mbind", | |
"set_mempolicy", | |
"get_mempolicy", | |
"mq_open", | |
"mq_unlink", | |
"mq_timedsend", | |
"mq_timedreceive", | |
"mq_notify", | |
"mq_getsetattr", | |
"kexec_load", | |
"waitid", | |
"add_key", | |
"request_key", | |
"keyctl", | |
"ioprio_set", | |
"ioprio_get", | |
"inotify_init", | |
"inotify_add_watch", | |
"inotify_rm_watch", | |
"migrate_pages", | |
"openat", | |
"mkdirat", | |
"mknodat", | |
"fchownat", | |
"futimesat", | |
"newfstatat", | |
"unlinkat", | |
"renameat", | |
"linkat", | |
"symlinkat", | |
"readlinkat", | |
"fchmodat", | |
"faccessat", | |
"pselect6", | |
"ppoll", | |
"unshare", | |
"set_robust_list", | |
"get_robust_list", | |
"splice", | |
"tee", | |
"sync_file_range", | |
"vmsplice", | |
"move_pages", | |
"utimensat", | |
"epoll_pwait", | |
"signalfd", | |
"timerfd_create", | |
"eventfd", | |
"fallocate", | |
"timerfd_settime", | |
"timerfd_gettime", | |
"accept4", | |
"signalfd4", | |
"eventfd2", | |
"epoll_create1", | |
"dup3", | |
"pipe2", | |
"inotify_init1", | |
"preadv", | |
"pwritev", | |
"rt_tgsigqueueinfo", | |
"perf_event_open", | |
"recvmmsg", | |
"fanotify_init", | |
"fanotify_mark", | |
"prlimit64", | |
"name_to_handle_at", | |
"open_by_handle_at", | |
"clock_adjtime", | |
"syncfs", | |
"sendmmsg", | |
"setns", | |
"getcpu", | |
"process_vm_readv", | |
"process_vm_writev", | |
"kcmp", | |
"finit_module", | |
) | |
BASE_SYSCALLS = ( | |
"capget", | |
"capset", | |
"chdir", | |
"fchown", | |
"futex", | |
"getdents64", | |
"getpid", | |
"getppid", | |
"lstat", | |
"openat", | |
"prctl", | |
"setgid", | |
"setgroups", | |
"setuid", | |
"stat", | |
) | |
if __name__ == "__main__": | |
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# readonly LOG_FILE=/tmp/syscall2seccomp.log | |
# touch $LOG_FILE | |
# exec 1>>$LOG_FILE | |
# exec 2>&1 | |
RED='\033[0;31m' | |
GREEN='\033[0;32m' | |
ORANGE='\033[0;33m' | |
NC='\033[0m' # No Color | |
CONTAINER_NAME=$1 | |
PYTHON_SCRIPT=$(pwd)/syscall2seccomp.py | |
LEARN_DURATION=3600 | |
PYTHON_MIN_VERSION='3.6' | |
declare -a deps=(sysdig curl docker) | |
function loginfo() | |
{ | |
echo -n; | |
echo -e "[$(date '+%F %R')] ${ORANGE}>${NC} $@" | |
} | |
function logerr() | |
{ | |
echo -e "[$(date '+%F %R')] ${RED}x${NC} $@" | |
exit 1 | |
} | |
function logok() | |
{ | |
echo -e "[$(date '+%F %R')] ${GREEN}✔${NC} $@" | |
} | |
function py_ver_test() | |
{ | |
if [ -z "${1}" ]; then return 1 ; fi | |
dpkg --compare-versions $(${1} -c 'import platform; print(platform.python_version())') ge ${2} | |
return $? | |
} | |
function install_sysdig() | |
{ | |
read -t 15 -n 1 -p "Install sysdig? (y/N) " decision | |
if [ "$decision" != "Y" ] && [ "$decision" != "y" ]; then | |
return 1 | |
fi | |
curl -s https://s3.amazonaws.com/download.draios.com/stable/install-sysdig | bash | |
read -t 15 -n 1 -p "Add current user to the sysdig group? (y/N) " decision | |
if [ "$decision" == "Y" ] || [ "$decision" == "y" ]; then | |
groupadd sysdig | |
usermod -aG sysdig $(id -un) | |
fi | |
read -t 15 -n 1 -p "Restart NOW to take effect? (y/N) " decision | |
if [ "$decision" == "Y" ] || [ "$decision" == "y" ]; then | |
shutdown -r now | |
fi | |
return 0 | |
} | |
TRACKING_PID="" | |
function pressed_interupt() | |
{ | |
loginfo "Pressed Enter" | |
if [[ ! -z "${TRACKING_PID}" ]]; then | |
loginfo "Killing sysdig PID ${TRACKING_PID}" | |
kill -SIGQUIT ${TRACKING_PID} | |
fi | |
} | |
function learn() | |
{ | |
sysdig container.name=${1} > /tmp/${1}.sysdig & | |
TRACKING_PID=$! | |
loginfo "sysdig PID ${TRACKING_PID}" | |
loginfo "Starting ${1}" | |
docker start ${1} >/dev/null 2>&1 | |
trap pressed_interupt SIGINT | |
loginfo "Interact with the ${1} container for the next ${LEARN_DURATION} seconds" | |
loginfo "Or press CTRL+C to continue when ready" | |
sleep ${LEARN_DURATION} | |
return 0 | |
} | |
function convert() | |
{ | |
${PYTHON_SCRIPT} -s /tmp/${1}.sysdig > ${1}-seccomp.json | |
return $? | |
} | |
if [[ $EUID -ne 0 ]]; then | |
logerr "This script must be run as root" | |
fi | |
if [[ -z "${CONTAINER_NAME}" ]]; then | |
logerr "container name needs to be provided as an argument" | |
fi | |
if [[ ! -f "${PYTHON_SCRIPT}" ]]; then | |
logerr "could not find script at: ${PYTHON_SCRIPT}" | |
fi | |
py=$(which python) | |
if ! py_ver_test ${py} ${PYTHON_MIN_VERSION} ; then | |
py=$(which python3) | |
py_ver_test ${py} ${PYTHON_MIN_VERSION} || logerr "Python does not meet minimum requirement v${PYTHON_MIN_VERSION}" | |
fi | |
logok "Using ${py}" | |
for cmd in ${deps[@]}; do | |
cmd_path=$(which ${cmd}) | |
if [ -z "${cmd_path}" ]; then | |
if [ ${cmd} == "sysdig" ]; then | |
logerr "${cmd} not found" | |
install_sysdig | |
fi | |
fi | |
logok "Using ${cmd_path}" | |
done | |
loginfo "Starting sysdig" | |
learn ${CONTAINER_NAME} | |
convert ${CONTAINER_NAME} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment