4
4
from rich .console import Console
5
5
from rich import box
6
6
from rich .text import Text
7
+ from rich .style import Style
8
+
9
+
10
+ DANGEROUS_SYSCALLS = ["acct" , "add_key" , "bpf" , "clock_adjtime" , "clone" , "create_module" ,
11
+ "delete_module" , "finit_module" , "get_kernel_syms" , "get_mempolicy" ,
12
+ "init_module" , "ioperm" , "iopl" , "kcmp" , "kexec_file_load" , "kexec_load" ,
13
+ "keyctl" , "lookup_dcookie" , "mbind" , "mount" , "move_pages" , "nfsservctl" ,
14
+ "open_by_handle_at" , "perf_event_open" , "personality" , "pivot_root" ,
15
+ "process_vm_readv" , "process_vm_writev" , "ptrace" , "query_module" ,
16
+ "quotactl" , "reboot" , "request_key" , "set_mempolicy" , "setns" ,
17
+ "settimeofday" , "stime" , "swapon" , "swapoff" , "sysfs" , "_sysctl" , "umount" ,
18
+ "umount2" , "unshare" , "uselib" , "userfaultfd" , "ustat" , "vm86" , "vm86old" ]
19
+
20
+ def reduce_action (action ):
21
+ ALLOW = ["ALLOW" , action , "permissive" ]
22
+ DENY = ["DENY" , action , "restrictive" ]
23
+ CONDITION = ["CONDITION" , action , "restrictive" ]
24
+ UNKNOWN = ["ERROR-NOT-FOUND" , action , "permissive" ]
25
+ ACTION_MAP = {
26
+ "ALLOW" : ALLOW ,
27
+ "ERRNO" : DENY ,
28
+ "ALLOW/ERRORNO" : CONDITION ,
29
+ "N/A" : ALLOW ,
30
+ "LOG" : ALLOW ,
31
+ "KILL" : DENY ,
32
+ "TRACE" : CONDITION ,
33
+ "TRAP" : CONDITION ,
34
+ "Unknown" : UNKNOWN ,
35
+ }
36
+ # Check error numbers
37
+ if action .startswith ("ERRNO" ):
38
+ return DENY
39
+ elif "/" in action and action != "N/A" :
40
+ return CONDITION
41
+ return ACTION_MAP .get (action , f"ERROR MAPPING EFFECTIVE PERMISSIONS { action } " )
42
+
7
43
8
44
def is_convertible_to_int (s ):
9
45
"""Check if a string can be safely converted to an integer."""
@@ -13,66 +49,51 @@ def is_convertible_to_int(s):
13
49
except ValueError :
14
50
return False
15
51
16
- dangerous_syscalls = [
17
- "acct" , "add_key" , "bpf" , "clock_adjtime" , "clone" , "create_module" ,
18
- "delete_module" , "finit_module" , "get_kernel_syms" , "get_mempolicy" ,
19
- "init_module" , "ioperm" , "iopl" , "kcmp" , "kexec_file_load" , "kexec_load" ,
20
- "keyctl" , "lookup_dcookie" , "mbind" , "mount" , "move_pages" , "nfsservctl" ,
21
- "open_by_handle_at" , "perf_event_open" , "personality" , "pivot_root" ,
22
- "process_vm_readv" , "process_vm_writev" , "ptrace" , "query_module" ,
23
- "quotactl" , "reboot" , "request_key" , "set_mempolicy" , "setns" ,
24
- "settimeofday" , "stime" , "swapon" , "swapoff" , "sysfs" , "_sysctl" , "umount" ,
25
- "umount2" , "unshare" , "uselib" , "userfaultfd" , "ustat" , "vm86" , "vm86old"
26
- ]
27
-
28
- def get_seccomp_policy (container1 ):
29
- full1 , d1 = get_seccomp_filters (container1 ["pid" ])
30
- if d1 :
31
- container1 ["summary" ] = d1 .syscallSummary
32
- else :
33
- container1 ["summary" ] = {}
34
-
35
- da1 = d1 .defaultAction
36
-
37
- console = Console ()
38
- table = Table (show_header = True , show_lines = True , box = box .HEAVY_EDGE , style = "green" , pad_edge = False )
39
- table .add_column (header = "Container" , justify = "left" , min_width = 20 )
40
- table .add_column (header = container1 ["name" ], justify = "left" , max_width = 20 , overflow = None )
41
- table .add_column (header = container2 ["name" ], justify = "left" , max_width = 20 , overflow = None )
42
-
43
- # Add Seccomp and Capabilities Information
44
- table .add_custom_row ("[b]seccomp" , container1 ["seccomp" ])
45
- table .add_custom_row ("[b]caps" , container1 ["caps" ], end_section = True )
46
-
47
- # Iterate through the global SYSCALLS dict
48
- for syscall_num , syscall_info in SYSCALLS .items ():
49
- syscall_name = syscall_info [1 ]
50
-
51
- # Determine effective policy for container1
52
- if syscall_name in container1 ["summary" ]:
53
- action1 = container1 ["summary" ][syscall_name ].get ("action" , da1 )
54
- count1 = container1 ["summary" ][syscall_name ].get ("count" , 0 )
55
- effective_policy1 = f"{ action1 } "
52
+ def get_seccomp_policy (container ):
53
+ """Get the seccomp policy and return a detailed table for every syscall."""
54
+ try :
55
+ full , d = get_seccomp_filters (container ["pid" ])
56
+ if d :
57
+ container ["summary" ] = d .syscallSummary
56
58
else :
57
- effective_policy1 = f" { da1 } "
59
+ container [ "summary" ] = {}
58
60
59
- table . add_custom_row ( syscall_name , effective_policy1 )
61
+ default_action = d . defaultAction if d else "unknown"
60
62
61
- # Add total instructions row
62
- container1 ["total" ] = container1 ["summary" ].get ("total" , {"count" : 0 }).get ("count" )
63
- table .add_custom_row ("Total Instructions" , str (container1 ["total" ]))
63
+ console = Console ()
64
+ table = Table (show_header = True , show_lines = True , box = box .HEAVY_EDGE , style = "green" , pad_edge = False )
65
+ table .add_column (header = "Syscall" , justify = "left" , min_width = 20 )
66
+ table .add_column (header = f"{ container ['name' ]} Action" , justify = "left" , min_width = 20 )
67
+
68
+ # Iterate through all syscalls in SYSCALLS
69
+ for syscall_num , syscall_info in SYSCALLS .items ():
70
+ syscall_name = syscall_info [1 ]
71
+
72
+ # Get the action for this syscall from the container's summary, or default to the default action
73
+ if syscall_name in container ["summary" ]:
74
+ action = container ["summary" ][syscall_name ].get ("action" , default_action )
75
+ else :
76
+ action = default_action
77
+
78
+ table .add_custom_row (syscall_name , action )
79
+
80
+ return table , full
81
+
82
+ except Exception as e :
83
+ console .print (f"An error occurred: { e } " , style = "bold red" )
84
+ return None , None
85
+
86
+ def compare_seccomp_policies (container1 , container2 , reduce = True , only_diff = True , only_dangerous = False ):
87
+ """Compare the seccomp policies of two containers and return a detailed table."""
64
88
65
- return table , full1
89
+ danger_style = Style ( color = "red" , blink = True , bold = True )
66
90
67
- def compare_seccomp_policies ( container1 , container2 , full = False ):
91
+
68
92
try :
69
- # Extract SeccompSummary for both PIDs
70
93
full1 , d1 = get_seccomp_filters (container1 ["pid" ])
71
94
full2 , d2 = get_seccomp_filters (container2 ["pid" ])
72
-
73
-
74
95
75
- if d1 :
96
+ if d1 :
76
97
container1 ["summary" ] = d1 .syscallSummary
77
98
else :
78
99
container1 ["summary" ] = {}
@@ -82,54 +103,69 @@ def compare_seccomp_policies(container1, container2, full=False):
82
103
else :
83
104
container2 ["summary" ] = {}
84
105
85
- da1 = d1 .defaultAction
86
- da2 = d2 .defaultAction
106
+ default_action1 = d1 .defaultAction if d1 else "unknown"
107
+ default_action2 = d2 .defaultAction if d2 else "unknown"
87
108
88
109
console = Console ()
89
110
table = Table (show_header = True , show_lines = True , box = box .HEAVY_EDGE , style = "green" , pad_edge = False )
90
- table .add_column (header = "Container " , justify = "left" , min_width = 20 )
91
- table .add_column (header = container1 [" name" ] , justify = "left" , max_width = 20 , overflow = None )
92
- table .add_column (header = container2 [" name" ] , justify = "left" , max_width = 20 , overflow = None )
93
-
111
+ table .add_column (header = "Syscall " , justify = "left" , min_width = 20 )
112
+ table .add_column (header = f" { container1 [' name' ] } Action" , justify = "left" , min_width = 20 )
113
+ table .add_column (header = f" { container2 [' name' ] } Action" , justify = "left" , min_width = 20 )
114
+
94
115
# Add Seccomp and Capabilities Information
95
- table .add_custom_row ("[b]seccomp" , container1 ["seccomp" ], container2 ["seccomp" ])
96
- table .add_custom_row ("[b]caps" , container1 ["caps" ], container2 ["caps" ], end_section = True )
116
+ table .add_custom_row ("[b]seccomp" , container1 ["seccomp" ])
117
+ table .add_custom_row ("[b]caps" , container1 ["caps" ], end_section = True )
118
+
97
119
98
- # Iterate through the global SYSCALLS dict
120
+ # Iterate through all syscalls in SYSCALLS
99
121
for syscall_num , syscall_info in SYSCALLS .items ():
100
122
syscall_name = syscall_info [1 ]
123
+
124
+ if only_dangerous and not syscall_name in DANGEROUS_SYSCALLS :
125
+ continue
126
+
127
+
101
128
102
- # Determine effective policy for container1
129
+ # Get the action for container1
103
130
if syscall_name in container1 ["summary" ]:
104
- action1 = container1 ["summary" ][syscall_name ].get ("action" , da1 )
105
- count1 = container1 ["summary" ][syscall_name ].get ("count" , 0 )
106
- effective_policy1 = f"{ action1 } "
131
+ action1 = container1 ["summary" ][syscall_name ].get ("action" , default_action1 )
107
132
else :
108
- effective_policy1 = f" { da1 } "
133
+ action1 = default_action1
109
134
110
- # Determine effective policy for container2
135
+ # Get the action for container2
111
136
if syscall_name in container2 ["summary" ]:
112
- action2 = container2 ["summary" ][syscall_name ].get ("action" , da2 )
113
- count2 = container2 ["summary" ][syscall_name ].get ("count" , 0 )
114
- effective_policy2 = f"{ action2 } "
137
+ action2 = container2 ["summary" ][syscall_name ].get ("action" , default_action2 )
115
138
else :
116
- effective_policy2 = f"{ da2 } "
117
-
118
- # Compare effective policies
119
- if effective_policy1 == effective_policy2 :
120
- continue # Skip identical policies
121
-
122
- table .add_custom_row (syscall_name , effective_policy1 , effective_policy2 )
123
-
124
- # Add total instructions row
125
- container1 ["total" ] = container1 ["summary" ].get ("total" , {"count" : 0 }).get ("count" )
126
- container2 ["total" ] = container2 ["summary" ].get ("total" , {"count" : 0 }).get ("count" )
127
- table .add_custom_row ("Total Instructions" , str (container1 ["total" ]), str (container2 ["total" ]))
128
-
129
- if len (table .rows ) <= 3 and container1 ["total" ] == container2 ["total" ]:
130
- console .print (Text ("No seccomp filter differences were found between the two containers" , justify = "center" ))
139
+ action2 = default_action2
140
+
141
+ # Reduce the action to an effecctive action of allow or deny
142
+ if reduce :
143
+ action1 = reduce_action (action1 )[0 ]
144
+ action2 = reduce_action (action2 )[0 ]
145
+
146
+ # Skip identical policies if only_diff is True
147
+ if only_diff and action1 == action2 :
148
+ continue
149
+
150
+ if syscall_name in DANGEROUS_SYSCALLS :
151
+ syscall_name = f":warning:{ syscall_name } "
152
+
153
+ # Add row to table
154
+ table .add_custom_row (syscall_name , action1 , action2 )
131
155
132
- return table , full1 , full2
156
+ except Exception as e :
157
+ console .print (f"An error occurred: { e } " , style = "bold red" )
158
+ return None , None , None
159
+
160
+ # Add total instructions row
161
+ container1 ["total" ] = container1 ["summary" ].get ("total" , {"count" : 0 }).get ("count" )
162
+ container2 ["total" ] = container2 ["summary" ].get ("total" , {"count" : 0 }).get ("count" )
163
+ table .add_custom_row ("Total Instructions" , str (container1 ["total" ]), str (container2 ["total" ]))
164
+ if len (table .rows ) <= 3 and container1 ["total" ] == container2 ["total" ]:
165
+ console .print (Text ("No seccomp filter differences were found between the two containers" , justify = "center" ))
166
+
167
+ return table , full1 , full2
133
168
134
- except ValueError as e :
135
- print (f"An error occurred: { e } " )
169
+
170
+
171
+
0 commit comments