Class: Seccomp::Filter
- Inherits:
-
Object
- Object
- Seccomp::Filter
- Includes:
- MonitorMixin, Arg::DSL, Attributes, DSL
- Defined in:
- lib/seccomp/filter.rb
Overview
Mutable seccomp filter with automatic native-resource management.
Constant Summary
Constants included from Attributes
Attributes::ATTRIBUTE_API_LEVELS, Attributes::ATTRIBUTE_NAMES, Attributes::BOOLEAN_ATTRIBUTES
Class Method Summary collapse
-
.open(default_action = :kill_process) {|Filter| ... } ⇒ Object, Filter
Block result, or a filter without a block.
Instance Method Summary collapse
-
#add_arch(arch) ⇒ Filter
Receiver.
-
#add_rule(action, syscall, *comparisons) ⇒ Filter
Receiver.
-
#add_rule_exact(action, syscall, *comparisons) ⇒ Filter
Receiver.
-
#allow(syscall, *arguments) ⇒ Filter
Receiver.
-
#arch?(arch) ⇒ Boolean
Whether present.
-
#archs ⇒ Array<Symbol>
Configured architectures.
- #close ⇒ nil
-
#closed? ⇒ Boolean
Whether native resources were released.
-
#deny(syscall, *comparisons, errno: Errno::EPERM) ⇒ Filter
Receiver.
- #export_bpf(io) ⇒ nil
- #export_pfc(io) ⇒ nil
- #initialize(default_action = :kill_process) ⇒ void constructor
-
#kill(syscall, *comparisons) ⇒ Filter
Receiver.
- #load! ⇒ nil
-
#loaded? ⇒ Boolean
Whether load succeeded at least once.
-
#log(syscall, *comparisons) ⇒ Filter
Receiver.
-
#merge!(other) ⇒ Filter
Receiver.
-
#notifier ⇒ Notifier
Notification listener.
-
#notify(syscall, *comparisons) ⇒ Filter
Receiver.
-
#precompute ⇒ Filter
Receiver.
-
#priority(syscall, value) ⇒ Filter
Receiver.
-
#remove_arch(arch) ⇒ Filter
Receiver.
-
#reset(default_action = nil) ⇒ Filter
Receiver.
-
#to_bpf ⇒ String
Binary BPF program.
-
#to_pfc ⇒ String
Pseudo filter code.
-
#trace(syscall, value, *comparisons) ⇒ Filter
Receiver.
-
#transaction { ... } ⇒ Object
Block result.
-
#trap(syscall, *comparisons) ⇒ Filter
Receiver.
Methods included from DSL
Methods included from Arg::DSL
Methods included from Attributes
#[], #[]=, #bad_arch_action, #bad_arch_action=, #default_action, #optimize, #optimize=
Constructor Details
#initialize(default_action = :kill_process) ⇒ void
20 21 22 23 24 25 26 27 |
# File 'lib/seccomp/filter.rb', line 20 def initialize(default_action = :kill_process) super() @default_action = Action.resolve(default_action) @notification_rule = @default_action == Action::NOTIFY @listener_active = false @notification_receive_lock = Mutex.new @context = LowLevel.init(@default_action) end |
Class Method Details
.open(default_action = :kill_process) {|Filter| ... } ⇒ Object, Filter
Returns block result, or a filter without a block.
34 35 36 37 38 39 40 41 42 43 |
# File 'lib/seccomp/filter.rb', line 34 def self.open(default_action = :kill_process) filter = new(default_action) return filter unless block_given? begin yield filter ensure filter.close end end |
Instance Method Details
#add_arch(arch) ⇒ Filter
Returns receiver.
106 107 108 |
# File 'lib/seccomp/filter.rb', line 106 def add_arch(arch) change_arch(:arch_add, arch) end |
#add_rule(action, syscall, *comparisons) ⇒ Filter
Returns receiver.
145 146 147 |
# File 'lib/seccomp/filter.rb', line 145 def add_rule(action, syscall, *comparisons) add_rule_with(:rule_add_array, action, syscall, comparisons) end |
#add_rule_exact(action, syscall, *comparisons) ⇒ Filter
Returns receiver.
155 156 157 |
# File 'lib/seccomp/filter.rb', line 155 def add_rule_exact(action, syscall, *comparisons) add_rule_with(:rule_add_exact_array, action, syscall, comparisons) end |
#allow(syscall, *arguments) ⇒ Filter
Returns receiver.
164 165 166 167 168 169 170 171 |
# File 'lib/seccomp/filter.rb', line 164 def allow(syscall, *arguments) comparisons = arguments.drop_while { |argument| !argument.is_a?(ArgCmp) } syscalls = [syscall, *arguments.take(arguments.length - comparisons.length)] raise TypeError, "syscalls must precede comparisons" unless comparisons.all?(ArgCmp) synchronize { syscalls.each { |name| add_rule(:allow, name, *comparisons) } } self end |
#arch?(arch) ⇒ Boolean
Returns whether present.
122 123 124 125 126 127 128 129 130 |
# File 'lib/seccomp/filter.rb', line 122 def arch?(arch) synchronize do result = LowLevel.arch_exist(@context, Arch.resolve(arch)) return true if result.zero? return false if result == -Errno::EEXIST::Errno Error.check!(result, call: "seccomp_arch_exist", hint: "architecture #{arch}") end end |
#archs ⇒ Array<Symbol>
Returns configured architectures.
135 136 137 |
# File 'lib/seccomp/filter.rb', line 135 def archs synchronize { Arch.all.keys.select { |arch| arch != :native && arch?(arch) } } end |
#close ⇒ nil
47 48 49 50 51 52 53 54 55 |
# File 'lib/seccomp/filter.rb', line 47 def close NOTIFICATION_MONITOR.synchronize do synchronize do LowLevel.release(@context) @listener_active = false nil end end end |
#closed? ⇒ Boolean
Returns whether native resources were released.
59 60 61 |
# File 'lib/seccomp/filter.rb', line 59 def closed? synchronize { LowLevel.closed?(@context) } end |
#deny(syscall, *comparisons, errno: Errno::EPERM) ⇒ Filter
Returns receiver.
179 180 181 |
# File 'lib/seccomp/filter.rb', line 179 def deny(syscall, *comparisons, errno: Errno::EPERM) add_rule(Action.errno(errno), syscall, *comparisons) end |
#export_bpf(io) ⇒ nil
254 255 256 |
# File 'lib/seccomp/filter.rb', line 254 def export_bpf(io) export_to(:export_bpf, io) end |
#export_pfc(io) ⇒ nil
246 247 248 |
# File 'lib/seccomp/filter.rb', line 246 def export_pfc(io) export_to(:export_pfc, io) end |
#kill(syscall, *comparisons) ⇒ Filter
Returns receiver.
188 189 190 |
# File 'lib/seccomp/filter.rb', line 188 def kill(syscall, *comparisons) add_rule(:kill_process, syscall, *comparisons) end |
#load! ⇒ nil
293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/seccomp/filter.rb', line 293 def load! NOTIFICATION_MONITOR.synchronize do synchronize do ensure_notification_tsync_supported! if notification_action? && LowLevel.notify_fd(@context) >= 0 raise NotificationError, "a notification listener is already active" end Error.check!(LowLevel.load(@context), call: "seccomp_load") @listener_active ||= @notification_rule end end nil end |
#loaded? ⇒ Boolean
Returns whether load succeeded at least once.
310 311 312 |
# File 'lib/seccomp/filter.rb', line 310 def loaded? synchronize { LowLevel.loaded?(@context) } end |
#log(syscall, *comparisons) ⇒ Filter
Returns receiver.
197 198 199 |
# File 'lib/seccomp/filter.rb', line 197 def log(syscall, *comparisons) add_rule(:log, syscall, *comparisons) end |
#merge!(other) ⇒ Filter
Returns receiver.
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/seccomp/filter.rb', line 83 def merge!(other) raise TypeError, "other must be a Seccomp::Filter" unless other.is_a?(Filter) raise ArgumentError, "cannot merge a filter into itself" if equal?(other) first, second = [self, other].sort_by(&:object_id) first.synchronize do second.synchronize do notification_rule = @notification_rule || other.instance_variable_get(:@notification_rule) if notification_rule ensure_notification_tsync_supported!(notification_action: true, tsync: tsync? || other.tsync?) end Error.check!(LowLevel.merge(@context, other.__send__(:context)), call: "seccomp_merge") merge_state_from(other) end end self end |
#notifier ⇒ Notifier
Returns notification listener.
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 |
# File 'lib/seccomp/filter.rb', line 341 def notifier NOTIFICATION_MONITOR.synchronize do synchronize do raise NotificationError, "filter has not been loaded" unless loaded? unless @listener_active raise NotificationError, "filter has no active notification listener" end fd = LowLevel.notify_fd(@context) if fd.negative? raise NotificationError.new("seccomp_notify_fd failed", errno: -fd, call: "seccomp_notify_fd") end Notifier.new(self, fd) end end end |
#notify(syscall, *comparisons) ⇒ Filter
Returns receiver.
206 207 208 |
# File 'lib/seccomp/filter.rb', line 206 def notify(syscall, *comparisons) add_rule(:notify, syscall, *comparisons) end |
#precompute ⇒ Filter
Returns receiver.
283 284 285 286 287 288 |
# File 'lib/seccomp/filter.rb', line 283 def precompute synchronize do Error.check!(LowLevel.precompute(@context), call: "seccomp_precompute") end self end |
#priority(syscall, value) ⇒ Filter
Returns receiver.
234 235 236 237 238 239 240 |
# File 'lib/seccomp/filter.rb', line 234 def priority(syscall, value) synchronize do Error.check!(LowLevel.syscall_priority(@context, Syscall.number(syscall), value), call: "seccomp_syscall_priority", hint: "syscall #{syscall}") end self end |
#remove_arch(arch) ⇒ Filter
Returns receiver.
114 115 116 |
# File 'lib/seccomp/filter.rb', line 114 def remove_arch(arch) change_arch(:arch_remove, arch) end |
#reset(default_action = nil) ⇒ Filter
Returns receiver.
67 68 69 70 71 72 73 74 75 76 |
# File 'lib/seccomp/filter.rb', line 67 def reset(default_action = nil) synchronize do action = Action.resolve(default_action.nil? ? @default_action : default_action) ensure_notification_tsync_supported!(notification_action: action == Action::NOTIFY) Error.check!(LowLevel.reset(@context, action), call: "seccomp_reset") @default_action = action @notification_rule = action == Action::NOTIFY end self end |
#to_bpf ⇒ String
Returns binary BPF program.
268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/seccomp/filter.rb', line 268 def to_bpf if Seccomp.supports?(:bpf_mem) synchronize do rc, result = LowLevel.export_bpf_mem(@context) Error.check!(rc, call: "seccomp_export_bpf_mem") result end else export_string(:export_bpf, binmode: true) end end |
#to_pfc ⇒ String
Returns pseudo filter code.
261 262 263 |
# File 'lib/seccomp/filter.rb', line 261 def to_pfc export_string(:export_pfc) end |
#trace(syscall, value, *comparisons) ⇒ Filter
Returns receiver.
225 226 227 |
# File 'lib/seccomp/filter.rb', line 225 def trace(syscall, value, *comparisons) add_rule(Action.trace(value), syscall, *comparisons) end |
#transaction { ... } ⇒ Object
Returns block result.
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 |
# File 'lib/seccomp/filter.rb', line 318 def transaction synchronize do raise ArgumentError, "nested transactions are not allowed" if @in_transaction previous_notification_rule = @notification_rule begin Error.check!(LowLevel.transaction_start(@context), call: "seccomp_transaction_start") @in_transaction = true result = yield Error.check!(LowLevel.transaction_commit(@context), call: "seccomp_transaction_commit") committed = true result ensure LowLevel.transaction_reject(@context) if @in_transaction && !committed && !closed? @notification_rule = previous_notification_rule unless committed @in_transaction = false end end end |
#trap(syscall, *comparisons) ⇒ Filter
Returns receiver.
215 216 217 |
# File 'lib/seccomp/filter.rb', line 215 def trap(syscall, *comparisons) add_rule(:trap, syscall, *comparisons) end |