vivarium 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/examples/dlopen_demo_otel.rb +45 -0
- data/examples/env_access_external_demo_otel.rb +54 -0
- data/examples/env_access_ruby_demo_otel.rb +52 -0
- data/examples/execve_demo_otel.rb +55 -0
- data/examples/file_operation_demo_otel.rb +82 -0
- data/examples/network_client_demo_otel.rb +83 -0
- data/examples/network_client_demo_otel_out.rb +84 -0
- data/examples/privilege_event_demo_otel.rb +45 -0
- data/examples/raise_demo_otel.rb +47 -0
- data/examples/save_raw_demo_otel.rb +35 -0
- data/examples/signal_kill_demo_otel.rb +45 -0
- data/examples/ssl_write_demo_otel.rb +36 -0
- data/lib/vivarium/api_server.rb +26 -1
- data/lib/vivarium/cli.rb +101 -6
- data/lib/vivarium/correlator.rb +39 -1
- data/lib/vivarium/display_filter.rb +13 -2
- data/lib/vivarium/otel_exporter.rb +231 -0
- data/lib/vivarium/otel_stream.rb +277 -0
- data/lib/vivarium/raw_store.rb +27 -9
- data/lib/vivarium/tree_renderer.rb +19 -0
- data/lib/vivarium/version.rb +1 -1
- data/lib/vivarium.rb +130 -14
- metadata +15 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 00bd1e28a68f907293bb6bd0d23ceb26443fe603e653a18a8a88a4a36575b540
|
|
4
|
+
data.tar.gz: 440caf222aa2003056ff827b8da3a211da1ea3fc25aae1c237b433d040331abb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7120d43057fa8f532c6fcffd512d33b5392bd7fe7cf174811afd3fc737c515230b6e82cad2ea26b776b129817a350467f4b5beb007dcf009c58c2b6cf656227c
|
|
7
|
+
data.tar.gz: 2502ea29816994a2a8dfa05c65c9302da57f74b29790692111e23c5afdd093ba7e2c992b84ab218234cd3e9701df08ea885549023264c6a92868fc882a72c18f
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "fiddle"
|
|
5
|
+
require "vivarium"
|
|
6
|
+
|
|
7
|
+
FILTER = {
|
|
8
|
+
include_events: %w[dlopen mmap_exec]
|
|
9
|
+
}.freeze
|
|
10
|
+
|
|
11
|
+
OTEL_ENDPOINT = ENV.fetch("VIVARIUM_OTEL_ENDPOINT", "http://localhost:4318/v1/traces")
|
|
12
|
+
|
|
13
|
+
# Usage:
|
|
14
|
+
# 1) In another shell (root): sudo bundle exec vivariumd
|
|
15
|
+
# (dlopen uprobe is attached automatically when libc is found)
|
|
16
|
+
# 2) Run this script: bundle exec ruby examples/dlopen_demo_otel.rb
|
|
17
|
+
# or: VIVARIUM_OTEL_ENDPOINT=http://collector:4318/v1/traces bundle exec ruby examples/dlopen_demo_otel.rb
|
|
18
|
+
#
|
|
19
|
+
# You can disable the dlopen uprobe with `sudo vivariumd --no-dlopen-trace`
|
|
20
|
+
# or point at a specific libc with `sudo vivariumd --libc /lib/x86_64-linux-gnu/libc.so.6`.
|
|
21
|
+
|
|
22
|
+
Vivarium.observe(filter: FILTER, otel_endpoint: OTEL_ENDPOINT) do
|
|
23
|
+
begin
|
|
24
|
+
libm = Fiddle.dlopen("libm.so.6")
|
|
25
|
+
sin_fn = Fiddle::Function.new(libm["sin"], [Fiddle::TYPE_DOUBLE], Fiddle::TYPE_DOUBLE)
|
|
26
|
+
puts "[dlopen_demo] sin(PI/4) = #{sin_fn.call(Math::PI / 4).round(6)}"
|
|
27
|
+
libm.close
|
|
28
|
+
rescue Fiddle::DLError => e
|
|
29
|
+
warn "[dlopen_demo] libm: #{e.message}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
begin
|
|
33
|
+
libsqlite3 = Fiddle.dlopen("libsqlite3.so.0")
|
|
34
|
+
puts "[dlopen_demo] libsqlite3 loaded: version = #{Fiddle::Function.new(libsqlite3["sqlite3_libversion"], [], Fiddle::TYPE_VOIDP).call}"
|
|
35
|
+
libsqlite3.close
|
|
36
|
+
rescue Fiddle::DLError => e
|
|
37
|
+
warn "[dlopen_demo] libsqlite3: #{e.message}"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
Bundler.with_unbundled_env do
|
|
41
|
+
system("ruby -e 'require \"fiddle\"; Fiddle.dlopen(\"libm.so.6\").close'")
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
puts "[dlopen_demo] done"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "rbconfig"
|
|
5
|
+
require "vivarium"
|
|
6
|
+
|
|
7
|
+
FILTER = {
|
|
8
|
+
include_events: %w[env_caccess proc_fork proc_exec]
|
|
9
|
+
}.freeze
|
|
10
|
+
|
|
11
|
+
OTEL_ENDPOINT = ENV.fetch("VIVARIUM_OTEL_ENDPOINT", "http://localhost:4318/v1/traces")
|
|
12
|
+
|
|
13
|
+
# Usage:
|
|
14
|
+
# 1) In another shell (root): sudo bundle exec vivariumd
|
|
15
|
+
# 2) Run this script: bundle exec ruby examples/env_access_external_demo_otel.rb
|
|
16
|
+
# or: VIVARIUM_OTEL_ENDPOINT=http://collector:4318/v1/traces bundle exec ruby examples/env_access_external_demo_otel.rb
|
|
17
|
+
#
|
|
18
|
+
# This demo launches an external Ruby process and forces direct libc calls to
|
|
19
|
+
# getenv/setenv/unsetenv/putenv/clearenv through Fiddle.
|
|
20
|
+
|
|
21
|
+
CHILD_CODE = <<~RUBY
|
|
22
|
+
require "fiddle"
|
|
23
|
+
|
|
24
|
+
libc = begin
|
|
25
|
+
Fiddle.dlopen("libc.so.6")
|
|
26
|
+
rescue Fiddle::DLError
|
|
27
|
+
Fiddle.dlopen(nil)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
getenv = Fiddle::Function.new(libc["getenv"], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_VOIDP)
|
|
31
|
+
setenv = Fiddle::Function.new(libc["setenv"], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP, Fiddle::TYPE_INT], Fiddle::TYPE_INT)
|
|
32
|
+
unsetenv = Fiddle::Function.new(libc["unsetenv"], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
|
|
33
|
+
putenv = Fiddle::Function.new(libc["putenv"], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
|
|
34
|
+
clearenv = Fiddle::Function.new(libc["clearenv"], [], Fiddle::TYPE_INT)
|
|
35
|
+
|
|
36
|
+
key = "VIVARIUM_ENV_EXT_DEMO"
|
|
37
|
+
putenv_buf = "VIVARIUM_ENV_EXT_PUT=from_putenv"
|
|
38
|
+
|
|
39
|
+
getenv.call("HOME")
|
|
40
|
+
setenv.call(key, "from_setenv", 1)
|
|
41
|
+
getenv.call(key)
|
|
42
|
+
putenv.call(putenv_buf)
|
|
43
|
+
unsetenv.call(key)
|
|
44
|
+
clearenv.call
|
|
45
|
+
RUBY
|
|
46
|
+
|
|
47
|
+
Vivarium.observe(filter: FILTER, otel_endpoint: OTEL_ENDPOINT) do
|
|
48
|
+
puts "[env-external-demo] spawning external child"
|
|
49
|
+
pid = Process.spawn(RbConfig.ruby, "-e", CHILD_CODE)
|
|
50
|
+
Process.wait(pid)
|
|
51
|
+
puts "[env-external-demo] child exit status=#{Process.last_status.exitstatus}"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
puts "[env-external-demo] done"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "vivarium"
|
|
5
|
+
|
|
6
|
+
FILTER = {
|
|
7
|
+
include_events: %w[span_start span_stop env_caccess]
|
|
8
|
+
}.freeze
|
|
9
|
+
|
|
10
|
+
OTEL_ENDPOINT = ENV.fetch("VIVARIUM_OTEL_ENDPOINT", "http://localhost:4318/v1/traces")
|
|
11
|
+
|
|
12
|
+
# Usage:
|
|
13
|
+
# 1) In another shell (root): sudo bundle exec vivariumd
|
|
14
|
+
# 2) Run this script: bundle exec ruby examples/env_access_ruby_demo_otel.rb
|
|
15
|
+
# or: VIVARIUM_OTEL_ENDPOINT=http://collector:4318/v1/traces bundle exec ruby examples/env_access_ruby_demo_otel.rb
|
|
16
|
+
|
|
17
|
+
def safe_fetch(key)
|
|
18
|
+
ENV.fetch(key)
|
|
19
|
+
rescue KeyError
|
|
20
|
+
nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def demo_env_reads
|
|
24
|
+
ENV["HOME"]
|
|
25
|
+
safe_fetch("PATH")
|
|
26
|
+
ENV.key?("SHELL")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def demo_env_writes
|
|
30
|
+
ENV["VIVARIUM_ENV_DEMO_A"] = "1"
|
|
31
|
+
ENV.store("VIVARIUM_ENV_DEMO_B", "2")
|
|
32
|
+
ENV.delete("VIVARIUM_ENV_DEMO_A")
|
|
33
|
+
ENV.replace(ENV.to_h.merge("VIVARIUM_ENV_DEMO_C" => "3"))
|
|
34
|
+
ENV.delete("VIVARIUM_ENV_DEMO_B")
|
|
35
|
+
ENV.delete("VIVARIUM_ENV_DEMO_C")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
Vivarium.observe(filter: FILTER, otel_endpoint: OTEL_ENDPOINT) do
|
|
39
|
+
original_env = ENV.to_h
|
|
40
|
+
|
|
41
|
+
puts "[env-ruby-demo] read methods"
|
|
42
|
+
demo_env_reads
|
|
43
|
+
|
|
44
|
+
puts "[env-ruby-demo] write methods"
|
|
45
|
+
demo_env_writes
|
|
46
|
+
|
|
47
|
+
puts "[env-ruby-demo] clear"
|
|
48
|
+
ENV.clear
|
|
49
|
+
ENV.replace(original_env)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
puts "[env-ruby-demo] done"
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "tmpdir"
|
|
5
|
+
require "vivarium"
|
|
6
|
+
|
|
7
|
+
TMP_PREFIX = "vivarium-exec-demo"
|
|
8
|
+
FILTER = {
|
|
9
|
+
include_events: %w[proc_exec]
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
OTEL_ENDPOINT = ENV.fetch("VIVARIUM_OTEL_ENDPOINT", "http://localhost:4318/v1/traces")
|
|
13
|
+
|
|
14
|
+
# Usage:
|
|
15
|
+
# 1) In another shell (root): sudo bundle exec vivariumd
|
|
16
|
+
# 2) Run this script: bundle exec ruby examples/execve_demo_otel.rb
|
|
17
|
+
# or: VIVARIUM_OTEL_ENDPOINT=http://collector:4318/v1/traces bundle exec ruby examples/execve_demo_otel.rb
|
|
18
|
+
|
|
19
|
+
def try_step(title)
|
|
20
|
+
puts "[exec-demo] #{title}"
|
|
21
|
+
yield
|
|
22
|
+
rescue StandardError => e
|
|
23
|
+
puts "[exec-demo] #{title} failed: #{e.class}: #{e.message}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
Dir.mktmpdir(TMP_PREFIX, "/tmp") do |dir|
|
|
27
|
+
output_path = File.join(dir, "execve-demo.out")
|
|
28
|
+
|
|
29
|
+
Vivarium.observe(filter: FILTER, otel_endpoint: OTEL_ENDPOINT) do
|
|
30
|
+
try_step("system echo with multiple args") do
|
|
31
|
+
system("/bin/echo", "hello", "from", "vivarium", out: File::NULL)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
try_step("spawn env with explicit argv") do
|
|
35
|
+
pid = Process.spawn(
|
|
36
|
+
"/usr/bin/env",
|
|
37
|
+
"env",
|
|
38
|
+
"printf",
|
|
39
|
+
"execve-demo\n",
|
|
40
|
+
out: output_path,
|
|
41
|
+
err: File::NULL
|
|
42
|
+
)
|
|
43
|
+
Process.wait(pid)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
try_step("spawn sleep with flag") do
|
|
47
|
+
pid = Process.spawn("/bin/sleep", "0")
|
|
48
|
+
Process.wait(pid)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
puts "[exec-demo] output file: #{output_path}" if File.exist?(output_path)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
puts "[exec-demo] done"
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "tmpdir"
|
|
6
|
+
require "vivarium"
|
|
7
|
+
|
|
8
|
+
TMP_PREFIX = "vivarium-file-demo"
|
|
9
|
+
FILTER = {
|
|
10
|
+
include_events: %w[path_open file_symlink file_hardlink file_rename file_chmod file_unlink file_getdents]
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
OTEL_ENDPOINT = ENV.fetch("VIVARIUM_OTEL_ENDPOINT", "http://localhost:4318/v1/traces")
|
|
14
|
+
|
|
15
|
+
# Usage:
|
|
16
|
+
# 1) In another shell (root): sudo bundle exec vivariumd
|
|
17
|
+
# 2) Run this script: bundle exec ruby examples/file_operation_demo_otel.rb
|
|
18
|
+
# or: VIVARIUM_OTEL_ENDPOINT=http://collector:4318/v1/traces bundle exec ruby examples/file_operation_demo_otel.rb
|
|
19
|
+
|
|
20
|
+
def try_step(title)
|
|
21
|
+
puts "[file-demo] #{title}"
|
|
22
|
+
yield
|
|
23
|
+
rescue StandardError => e
|
|
24
|
+
puts "[file-demo] #{title} failed: #{e.class}: #{e.message}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
Dir.mktmpdir(TMP_PREFIX, "/tmp") do |dir|
|
|
28
|
+
source_path = File.join(dir, "source.txt")
|
|
29
|
+
renamed_path = File.join(dir, "renamed.txt")
|
|
30
|
+
hardlink_path = File.join(dir, "hardlink.txt")
|
|
31
|
+
symlink_path = File.join(dir, "symlink.txt")
|
|
32
|
+
|
|
33
|
+
Vivarium.observe(filter: FILTER, otel_endpoint: OTEL_ENDPOINT) do
|
|
34
|
+
try_step("create source file") do
|
|
35
|
+
File.write(source_path, "vivarium sample\n")
|
|
36
|
+
File.read(source_path)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
try_step("directory listing") do
|
|
40
|
+
Dir.children(dir)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
try_step("rename file") do
|
|
44
|
+
File.rename(source_path, renamed_path)
|
|
45
|
+
File.read(renamed_path)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
try_step("create hardlink") do
|
|
49
|
+
File.link(renamed_path, hardlink_path)
|
|
50
|
+
File.read(hardlink_path)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
try_step("create symlink") do
|
|
54
|
+
File.symlink(renamed_path, symlink_path)
|
|
55
|
+
File.read(symlink_path)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
try_step("chmod file") do
|
|
59
|
+
File.chmod(0o640, renamed_path)
|
|
60
|
+
File.stat(renamed_path)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
try_step("unlink hardlink") do
|
|
64
|
+
File.unlink(hardlink_path)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
try_step("unlink symlink") do
|
|
68
|
+
File.unlink(symlink_path)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
try_step("list directory again") do
|
|
72
|
+
Dir.children(dir)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
FileUtils.rm_f(symlink_path)
|
|
77
|
+
FileUtils.rm_f(hardlink_path)
|
|
78
|
+
FileUtils.rm_f(renamed_path)
|
|
79
|
+
FileUtils.rm_f(source_path)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
puts "[file-demo] done"
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "socket"
|
|
5
|
+
require "vivarium"
|
|
6
|
+
|
|
7
|
+
FILTER = {
|
|
8
|
+
include_events: %w[sock_connect dns_req odd_socket]
|
|
9
|
+
}.freeze
|
|
10
|
+
|
|
11
|
+
OTEL_ENDPOINT = ENV.fetch("VIVARIUM_OTEL_ENDPOINT", "http://localhost:4318/v1/traces")
|
|
12
|
+
|
|
13
|
+
# Usage:
|
|
14
|
+
# 1) In another shell (root): sudo bundle exec vivariumd
|
|
15
|
+
# 2) Run this script: bundle exec ruby examples/network_client_demo_otel.rb
|
|
16
|
+
# or: VIVARIUM_OTEL_ENDPOINT=http://collector:4318/v1/traces bundle exec ruby examples/network_client_demo_otel.rb
|
|
17
|
+
|
|
18
|
+
def try_step(title)
|
|
19
|
+
puts "[client] #{title}"
|
|
20
|
+
yield
|
|
21
|
+
rescue StandardError => e
|
|
22
|
+
puts "[client] #{title} failed: #{e.class}: #{e.message}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
Vivarium.observe(filter: FILTER, otel_endpoint: OTEL_ENDPOINT) do
|
|
26
|
+
# Likely emits sock_connect and dns_req via resolver traffic.
|
|
27
|
+
try_step("system: DNS lookup") do
|
|
28
|
+
system("getent hosts example.com >/dev/null 2>&1 || true")
|
|
29
|
+
system("getent hosts unknown.example.com >/dev/null 2>&1 || true")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Likely emits sock_connect through HTTPS connection attempts.
|
|
33
|
+
try_step("system: curl") do
|
|
34
|
+
system("curl -I https://example.com >/dev/null 2>&1 || true")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# ICMP example (may require CAP_NET_RAW / root depending on environment).
|
|
38
|
+
try_step("system: ping") do
|
|
39
|
+
system("ping -c 1 example.com >/dev/null 2>&1 || true")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Explicit connect path.
|
|
43
|
+
try_step("Ruby TCP connect") do
|
|
44
|
+
sock = TCPSocket.new("example.com", 80)
|
|
45
|
+
sock.close
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Raw DNS query payload, useful for dns_req decode testing.
|
|
49
|
+
try_step("Ruby UDP DNS query") do
|
|
50
|
+
dns_query = "\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00" +
|
|
51
|
+
"\x09udp-query\x07example\x03com\x00" +
|
|
52
|
+
"\x00\x01\x00\x01"
|
|
53
|
+
|
|
54
|
+
udp = UDPSocket.new
|
|
55
|
+
begin
|
|
56
|
+
udp.connect("127.0.0.53", 53)
|
|
57
|
+
rescue StandardError
|
|
58
|
+
udp.connect("8.8.8.8", 53)
|
|
59
|
+
end
|
|
60
|
+
udp.send(dns_query, 0)
|
|
61
|
+
udp.close
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Explicit sendto path for DNS payload visibility.
|
|
65
|
+
try_step("Ruby UDP sendto DNS query") do
|
|
66
|
+
dns_query = "\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00" +
|
|
67
|
+
"\x06sendto\x07example\x03com\x00" +
|
|
68
|
+
"\x00\x01\x00\x01"
|
|
69
|
+
|
|
70
|
+
udp = UDPSocket.new
|
|
71
|
+
udp.send(dns_query, 0, "127.0.0.53", 53)
|
|
72
|
+
udp.close
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Intentionally unusual socket type to trigger odd_socket.
|
|
76
|
+
try_step("Ruby odd socket attempt") do
|
|
77
|
+
af_packet = Socket.const_defined?(:AF_PACKET) ? Socket::AF_PACKET : 17
|
|
78
|
+
raw = Socket.new(af_packet, Socket::SOCK_RAW, 0)
|
|
79
|
+
raw.close
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
puts "[client] done"
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "socket"
|
|
5
|
+
require "vivarium"
|
|
6
|
+
|
|
7
|
+
FILTER = {
|
|
8
|
+
include_events: %w[sock_connect dns_req odd_socket]
|
|
9
|
+
}.freeze
|
|
10
|
+
|
|
11
|
+
OTEL_OUT = ENV.fetch("VIVARIUM_OTEL_OUT", "network_client_demo_otel.json")
|
|
12
|
+
|
|
13
|
+
# Usage:
|
|
14
|
+
# 1) In another shell (root): sudo bundle exec vivariumd
|
|
15
|
+
# 2) Run this script: bundle exec ruby examples/network_client_demo_otel_out.rb
|
|
16
|
+
# or: VIVARIUM_OTEL_OUT=/tmp/network_client_demo_otel.json bundle exec ruby examples/network_client_demo_otel_out.rb
|
|
17
|
+
|
|
18
|
+
def try_step(title)
|
|
19
|
+
puts "[client] #{title}"
|
|
20
|
+
yield
|
|
21
|
+
rescue StandardError => e
|
|
22
|
+
puts "[client] #{title} failed: #{e.class}: #{e.message}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
Vivarium.observe(filter: FILTER, otel_out: OTEL_OUT) do
|
|
26
|
+
# Likely emits sock_connect and dns_req via resolver traffic.
|
|
27
|
+
try_step("system: DNS lookup") do
|
|
28
|
+
system("getent hosts example.com >/dev/null 2>&1 || true")
|
|
29
|
+
system("getent hosts unknown.example.com >/dev/null 2>&1 || true")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Likely emits sock_connect through HTTPS connection attempts.
|
|
33
|
+
try_step("system: curl") do
|
|
34
|
+
system("curl -I https://example.com >/dev/null 2>&1 || true")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# ICMP example (may require CAP_NET_RAW / root depending on environment).
|
|
38
|
+
try_step("system: ping") do
|
|
39
|
+
system("ping -c 1 example.com >/dev/null 2>&1 || true")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Explicit connect path.
|
|
43
|
+
try_step("Ruby TCP connect") do
|
|
44
|
+
sock = TCPSocket.new("example.com", 80)
|
|
45
|
+
sock.close
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Raw DNS query payload, useful for dns_req decode testing.
|
|
49
|
+
try_step("Ruby UDP DNS query") do
|
|
50
|
+
dns_query = "\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00" +
|
|
51
|
+
"\x09udp-query\x07example\x03com\x00" +
|
|
52
|
+
"\x00\x01\x00\x01"
|
|
53
|
+
|
|
54
|
+
udp = UDPSocket.new
|
|
55
|
+
begin
|
|
56
|
+
udp.connect("127.0.0.53", 53)
|
|
57
|
+
rescue StandardError
|
|
58
|
+
udp.connect("8.8.8.8", 53)
|
|
59
|
+
end
|
|
60
|
+
udp.send(dns_query, 0)
|
|
61
|
+
udp.close
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Explicit sendto path for DNS payload visibility.
|
|
65
|
+
try_step("Ruby UDP sendto DNS query") do
|
|
66
|
+
dns_query = "\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00" +
|
|
67
|
+
"\x06sendto\x07example\x03com\x00" +
|
|
68
|
+
"\x00\x01\x00\x01"
|
|
69
|
+
|
|
70
|
+
udp = UDPSocket.new
|
|
71
|
+
udp.send(dns_query, 0, "127.0.0.53", 53)
|
|
72
|
+
udp.close
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Intentionally unusual socket type to trigger odd_socket.
|
|
76
|
+
try_step("Ruby odd socket attempt") do
|
|
77
|
+
af_packet = Socket.const_defined?(:AF_PACKET) ? Socket::AF_PACKET : 17
|
|
78
|
+
raw = Socket.new(af_packet, Socket::SOCK_RAW, 0)
|
|
79
|
+
raw.close
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
puts "[client] wrote OTLP JSON to #{OTEL_OUT}"
|
|
84
|
+
puts "[client] done"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "vivarium"
|
|
5
|
+
|
|
6
|
+
FILTER = {
|
|
7
|
+
include_events: %w[setid_change capable_check bprm_creds]
|
|
8
|
+
}.freeze
|
|
9
|
+
|
|
10
|
+
OTEL_ENDPOINT = ENV.fetch("VIVARIUM_OTEL_ENDPOINT", "http://localhost:4318/v1/traces")
|
|
11
|
+
|
|
12
|
+
# Usage:
|
|
13
|
+
# 1) In another shell (root): sudo bundle exec vivariumd
|
|
14
|
+
# 2) Run this script: bundle exec ruby examples/privilege_event_demo_otel.rb
|
|
15
|
+
# or: VIVARIUM_OTEL_ENDPOINT=http://collector:4318/v1/traces bundle exec ruby examples/privilege_event_demo_otel.rb
|
|
16
|
+
|
|
17
|
+
def try_step(title)
|
|
18
|
+
puts "[priv-demo] #{title}"
|
|
19
|
+
yield
|
|
20
|
+
rescue StandardError => e
|
|
21
|
+
puts "[priv-demo] #{title} failed: #{e.class}: #{e.message}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
Vivarium.observe(filter: FILTER, otel_endpoint: OTEL_ENDPOINT) do
|
|
25
|
+
try_step("attempt setuid(0)") do
|
|
26
|
+
Process::UID.change_privilege(0)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
try_step("attempt setgid(0)") do
|
|
30
|
+
Process::GID.change_privilege(0)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
try_step("attempt opening /etc/shadow") do
|
|
34
|
+
File.read("/etc/shadow")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
try_step("exec setuid-related binary") do
|
|
38
|
+
pid = Process.spawn("/usr/bin/sudo", "-n", "true", out: File::NULL, err: File::NULL)
|
|
39
|
+
Process.wait(pid)
|
|
40
|
+
rescue Errno::ENOENT
|
|
41
|
+
puts "[priv-demo] sudo not found; skipped"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
puts "[priv-demo] done"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "vivarium"
|
|
5
|
+
|
|
6
|
+
FILTER = {
|
|
7
|
+
include_events: %w[span_raise]
|
|
8
|
+
}.freeze
|
|
9
|
+
|
|
10
|
+
OTEL_ENDPOINT = ENV.fetch("VIVARIUM_OTEL_ENDPOINT", "http://localhost:4318/v1/traces")
|
|
11
|
+
|
|
12
|
+
def try_step(title)
|
|
13
|
+
puts "[priv-demo] #{title}"
|
|
14
|
+
yield
|
|
15
|
+
rescue StandardError => e
|
|
16
|
+
puts "[priv-demo] #{title} failed: #{e.class}: #{e.message}"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
Vivarium.observe(filter: FILTER, otel_endpoint: OTEL_ENDPOINT) do
|
|
20
|
+
try_step("raise in main") do
|
|
21
|
+
raise "error in main"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
try_step("raise in eval") do
|
|
25
|
+
eval("raise 'error in eval'")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
try_step("raise in nested eval") do
|
|
29
|
+
eval(<<~RUBY)
|
|
30
|
+
eval(<<~INNER_RUBY)
|
|
31
|
+
begin
|
|
32
|
+
eval(<<~INNER_INNER_RUBY)
|
|
33
|
+
puts "Hi"
|
|
34
|
+
raise "error in nested nested eval"
|
|
35
|
+
INNER_INNER_RUBY
|
|
36
|
+
rescue StandardError => _
|
|
37
|
+
puts "Rescued in nested eval"
|
|
38
|
+
end
|
|
39
|
+
File.open("/etc/hosts")
|
|
40
|
+
INNER_RUBY
|
|
41
|
+
RUBY
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
try_step("raise in method") do
|
|
45
|
+
File.open("notfound")
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "net/http"
|
|
5
|
+
require "uri"
|
|
6
|
+
require "vivarium"
|
|
7
|
+
|
|
8
|
+
OTEL_ENDPOINT = ENV.fetch("VIVARIUM_OTEL_ENDPOINT", "http://localhost:4318/v1/traces")
|
|
9
|
+
|
|
10
|
+
# Usage:
|
|
11
|
+
# 1) In another shell (root): sudo bundle exec vivariumd
|
|
12
|
+
# 2) Run this script: bundle exec ruby examples/save_raw_demo_otel.rb
|
|
13
|
+
# or: VIVARIUM_OTEL_ENDPOINT=http://collector:4318/v1/traces bundle exec ruby examples/save_raw_demo_otel.rb
|
|
14
|
+
#
|
|
15
|
+
# Note: this is the OTLP streaming variant of save_raw_demo.rb, so no vivraw file is written.
|
|
16
|
+
|
|
17
|
+
Vivarium.observe(otel_endpoint: OTEL_ENDPOINT) do
|
|
18
|
+
path = "/tmp/vivarium_save_raw_demo.txt"
|
|
19
|
+
File.write(path, "hello from save_raw demo\n")
|
|
20
|
+
File.chmod(0o600, path)
|
|
21
|
+
File.delete(path)
|
|
22
|
+
|
|
23
|
+
begin
|
|
24
|
+
uri = URI("https://udzura.jp/")
|
|
25
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
|
|
26
|
+
http.get(uri.request_uri)
|
|
27
|
+
end
|
|
28
|
+
rescue StandardError => e
|
|
29
|
+
warn "[save_raw_demo] Net::HTTP failed: #{e.class}: #{e.message}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
system("true")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
puts "[save_raw_demo] streamed OTLP events to #{OTEL_ENDPOINT}"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "vivarium"
|
|
5
|
+
|
|
6
|
+
FILTER = {
|
|
7
|
+
include_events: %w[task_kill]
|
|
8
|
+
}.freeze
|
|
9
|
+
|
|
10
|
+
OTEL_ENDPOINT = ENV.fetch("VIVARIUM_OTEL_ENDPOINT", "http://localhost:4318/v1/traces")
|
|
11
|
+
|
|
12
|
+
# Usage:
|
|
13
|
+
# 1) In another shell (root): sudo bundle exec vivariumd
|
|
14
|
+
# 2) Run this script: bundle exec ruby examples/signal_kill_demo_otel.rb
|
|
15
|
+
# or: VIVARIUM_OTEL_ENDPOINT=http://collector:4318/v1/traces bundle exec ruby examples/signal_kill_demo_otel.rb
|
|
16
|
+
|
|
17
|
+
def try_step(title)
|
|
18
|
+
puts "[signal-demo] #{title}"
|
|
19
|
+
yield
|
|
20
|
+
rescue StandardError => e
|
|
21
|
+
puts "[signal-demo] #{title} failed: #{e.class}: #{e.message}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
child_pid = nil
|
|
25
|
+
|
|
26
|
+
Vivarium.observe(filter: FILTER, otel_endpoint: OTEL_ENDPOINT) do
|
|
27
|
+
try_step("fork child process") do
|
|
28
|
+
child_pid = fork do
|
|
29
|
+
trap("TERM") { exit!(0) }
|
|
30
|
+
loop { sleep 1 }
|
|
31
|
+
end
|
|
32
|
+
puts "[signal-demo] child pid=#{child_pid}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
try_step("send TERM signal to child") do
|
|
36
|
+
sleep 0.1
|
|
37
|
+
Process.kill("TERM", child_pid)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
try_step("wait child process") do
|
|
41
|
+
Process.wait(child_pid)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
puts "[signal-demo] done"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "net/http"
|
|
5
|
+
require "uri"
|
|
6
|
+
require "vivarium"
|
|
7
|
+
|
|
8
|
+
FILTER = {
|
|
9
|
+
include_events: %w[ssl_write]
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
OTEL_ENDPOINT = ENV.fetch("VIVARIUM_OTEL_ENDPOINT", "http://localhost:4318/v1/traces")
|
|
13
|
+
|
|
14
|
+
# Usage:
|
|
15
|
+
# 1) In another shell (root): sudo bundle exec vivariumd
|
|
16
|
+
# (SSL_write uprobe is attached automatically when libssl is found)
|
|
17
|
+
# 2) Run this script: bundle exec ruby examples/ssl_write_demo_otel.rb
|
|
18
|
+
# or: VIVARIUM_OTEL_ENDPOINT=http://collector:4318/v1/traces bundle exec ruby examples/ssl_write_demo_otel.rb
|
|
19
|
+
#
|
|
20
|
+
# You can disable the SSL_write uprobe with `sudo vivariumd --no-ssl-trace`
|
|
21
|
+
# or point at a specific library with `sudo vivariumd --libssl /path/to/libssl.so.3`.
|
|
22
|
+
|
|
23
|
+
Vivarium.observe(filter: FILTER, otel_endpoint: OTEL_ENDPOINT) do
|
|
24
|
+
begin
|
|
25
|
+
uri = URI("https://udzura.jp/")
|
|
26
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
|
|
27
|
+
http.get(uri.request_uri)
|
|
28
|
+
end
|
|
29
|
+
rescue StandardError => e
|
|
30
|
+
warn "[ssl_demo] Net::HTTP failed: #{e.class}: #{e.message}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
system("curl -sS --http2 -o /dev/null https://nghttp2.org/ 2>/dev/null")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
puts "[ssl_demo] done"
|