Skip to content

Instantly share code, notes, and snippets.

@chrisdlangton
Last active August 31, 2022 23:51
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save chrisdlangton/f94fd36028a0ba46b73e6629524d5710 to your computer and use it in GitHub Desktop.
Save chrisdlangton/f94fd36028a0ba46b73e6629524d5710 to your computer and use it in GitHub Desktop.
Automatically generate seccomp profile json by learning from container activity using sysdig
#!/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()
#!/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