Module: Landlock

Defined in:
lib/landlock.rb,
lib/landlock/env.rb,
lib/landlock/errors.rb,
lib/landlock/native.rb,
lib/landlock/policy.rb,
lib/landlock/result.rb,
lib/landlock/rights.rb,
lib/landlock/runner.rb,
lib/landlock/rlimits.rb,
lib/landlock/version.rb,
lib/landlock/execution.rb,
lib/landlock/process_io.rb,
lib/landlock/validation.rb,
lib/landlock/runner/fork.rb,
lib/landlock/runner/native.rb,
ext/landlock/landlock.c

Defined Under Namespace

Modules: Env, Execution, Native, Policy, ProcessIO, ResultBehavior, Rlimits, Runner, Validation Classes: CaptureResult, CommandError, Error, OutputTooLargeError, SyscallError

Constant Summary collapse

UnsupportedError =
Class.new(Error)
FS_RIGHTS =
{
  execute: ACCESS_FS_EXECUTE,
  write_file: ACCESS_FS_WRITE_FILE,
  read_file: ACCESS_FS_READ_FILE,
  read_dir: ACCESS_FS_READ_DIR,
  remove_dir: ACCESS_FS_REMOVE_DIR,
  remove_file: ACCESS_FS_REMOVE_FILE,
  make_char: ACCESS_FS_MAKE_CHAR,
  make_dir: ACCESS_FS_MAKE_DIR,
  make_reg: ACCESS_FS_MAKE_REG,
  make_sock: ACCESS_FS_MAKE_SOCK,
  make_fifo: ACCESS_FS_MAKE_FIFO,
  make_block: ACCESS_FS_MAKE_BLOCK,
  make_sym: ACCESS_FS_MAKE_SYM,
  refer: ACCESS_FS_REFER,
  truncate: ACCESS_FS_TRUNCATE,
  ioctl_dev: ACCESS_FS_IOCTL_DEV
}.freeze
NET_RIGHTS =
{ bind_tcp: ACCESS_NET_BIND_TCP, connect_tcp: ACCESS_NET_CONNECT_TCP }.freeze
SCOPE_FLAGS =
{ abstract_unix_socket: SCOPE_ABSTRACT_UNIX_SOCKET, signal: SCOPE_SIGNAL }.freeze
READ_RIGHTS =
%i[read_file read_dir].freeze
EXEC_RIGHTS =
%i[execute read_file read_dir].freeze
WRITE_RIGHTS =
%i[
  read_file
  read_dir
  write_file
  truncate
  remove_dir
  remove_file
  make_char
  make_dir
  make_reg
  make_sock
  make_fifo
  make_block
  make_sym
  refer
].freeze
FILE_PATH_RIGHTS =
%i[execute write_file read_file truncate ioctl_dev].freeze
VERSION =
"0.3"
READ_CHUNK_BYTES =
16 * 1024
PROCESS_POLL_SECONDS =
0.1
STDIN_THREAD_JOIN_SECONDS =
0.1
POST_TIMEOUT_DRAIN_SECONDS =
0.05
ACCESS_FS_EXECUTE =
ULL2NUM(LANDLOCK_ACCESS_FS_EXECUTE)
ACCESS_FS_WRITE_FILE =
ULL2NUM(LANDLOCK_ACCESS_FS_WRITE_FILE)
ACCESS_FS_READ_FILE =
ULL2NUM(LANDLOCK_ACCESS_FS_READ_FILE)
ACCESS_FS_READ_DIR =
ULL2NUM(LANDLOCK_ACCESS_FS_READ_DIR)
ACCESS_FS_REMOVE_DIR =
ULL2NUM(LANDLOCK_ACCESS_FS_REMOVE_DIR)
ACCESS_FS_REMOVE_FILE =
ULL2NUM(LANDLOCK_ACCESS_FS_REMOVE_FILE)
ACCESS_FS_MAKE_CHAR =
ULL2NUM(LANDLOCK_ACCESS_FS_MAKE_CHAR)
ACCESS_FS_MAKE_DIR =
ULL2NUM(LANDLOCK_ACCESS_FS_MAKE_DIR)
ACCESS_FS_MAKE_REG =
ULL2NUM(LANDLOCK_ACCESS_FS_MAKE_REG)
ACCESS_FS_MAKE_SOCK =
ULL2NUM(LANDLOCK_ACCESS_FS_MAKE_SOCK)
ACCESS_FS_MAKE_FIFO =
ULL2NUM(LANDLOCK_ACCESS_FS_MAKE_FIFO)
ACCESS_FS_MAKE_BLOCK =
ULL2NUM(LANDLOCK_ACCESS_FS_MAKE_BLOCK)
ACCESS_FS_MAKE_SYM =
ULL2NUM(LANDLOCK_ACCESS_FS_MAKE_SYM)
ACCESS_FS_REFER =
ULL2NUM(LANDLOCK_ACCESS_FS_REFER)
ACCESS_FS_TRUNCATE =
ULL2NUM(LANDLOCK_ACCESS_FS_TRUNCATE)
ACCESS_FS_IOCTL_DEV =
ULL2NUM(LANDLOCK_ACCESS_FS_IOCTL_DEV)
ACCESS_NET_BIND_TCP =
ULL2NUM(LANDLOCK_ACCESS_NET_BIND_TCP)
ACCESS_NET_CONNECT_TCP =
ULL2NUM(LANDLOCK_ACCESS_NET_CONNECT_TCP)
SCOPE_ABSTRACT_UNIX_SOCKET =
ULL2NUM(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET)
SCOPE_SIGNAL =
ULL2NUM(LANDLOCK_SCOPE_SIGNAL)

Class Method Summary collapse

Class Method Details

._add_net_rule(ruleset_fd, port, access_bits) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'ext/landlock/landlock.c', line 81

static VALUE rb_ll_add_net_rule(VALUE self, VALUE ruleset_fd, VALUE port, VALUE access_bits) {
  unsigned long long p = NUM2ULL(port);
  if (p > 65535ULL) {
    rb_raise(rb_eArgError, "TCP port must be between 0 and 65535");
  }

  struct rb_landlock_net_port_attr rule;
  memset(&rule, 0, sizeof(rule));
  rule.allowed_access = NUM2ULL(access_bits);
  rule.port = p;

  long ret = ll_add_rule(NUM2INT(ruleset_fd), LANDLOCK_RULE_NET_PORT, &rule, 0);
  if (ret < 0) {
    raise_syscall_error("landlock_add_rule(net_port)");
  }
  return Qtrue;
}

._add_path_rule(ruleset_fd, path, access_bits) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'ext/landlock/landlock.c', line 56

static VALUE rb_ll_add_path_rule(VALUE self, VALUE ruleset_fd, VALUE path, VALUE access_bits) {
  int ruleset = NUM2INT(ruleset_fd);
  uint64_t allowed_access = NUM2ULL(access_bits);
  Check_Type(path, T_STRING);
  const char *cpath = StringValueCStr(path);
  int parent_fd = open(cpath, O_PATH | O_CLOEXEC);
  if (parent_fd < 0) {
    raise_syscall_error("open");
  }

  struct rb_landlock_path_beneath_attr rule;
  memset(&rule, 0, sizeof(rule));
  rule.allowed_access = allowed_access;
  rule.parent_fd = parent_fd;

  long ret = ll_add_rule(ruleset, LANDLOCK_RULE_PATH_BENEATH, &rule, 0);
  int saved_errno = errno;
  close(parent_fd);
  if (ret < 0) {
    errno = saved_errno;
    raise_syscall_error("landlock_add_rule(path_beneath)");
  }
  return Qtrue;
}

._close_fd(fd_value) ⇒ Object



116
117
118
119
120
121
122
# File 'ext/landlock/landlock.c', line 116

static VALUE rb_ll_close_fd(VALUE self, VALUE fd_value) {
  int fd = NUM2INT(fd_value);
  if (fd >= 0) {
    close(fd);
  }
  return Qnil;
}

._create_ruleset(*args) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'ext/landlock/landlock.c', line 30

static VALUE rb_ll_create_ruleset(int argc, VALUE *argv, VALUE self) {
  VALUE fs_bits, net_bits, scoped_bits;
  rb_scan_args(argc, argv, "21", &fs_bits, &net_bits, &scoped_bits);

  struct rb_landlock_ruleset_attr attr;
  uint64_t handled_access_net = NUM2ULL(net_bits);
  uint64_t scoped = NIL_P(scoped_bits) ? 0 : NUM2ULL(scoped_bits);
  size_t attr_size = offsetof(struct rb_landlock_ruleset_attr, handled_access_net);
  if (scoped != 0) {
    attr_size = sizeof(struct rb_landlock_ruleset_attr);
  } else if (handled_access_net != 0) {
    attr_size = offsetof(struct rb_landlock_ruleset_attr, scoped);
  }

  memset(&attr, 0, sizeof(attr));
  attr.handled_access_fs = NUM2ULL(fs_bits);
  attr.handled_access_net = handled_access_net;
  attr.scoped = scoped;

  long fd = ll_create_ruleset(&attr, attr_size, 0);
  if (fd < 0) {
    raise_syscall_error("landlock_create_ruleset");
  }
  return INT2NUM(fd);
}

._restrict_self(ruleset_fd) ⇒ Object



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'ext/landlock/landlock.c', line 99

static VALUE rb_ll_restrict_self(VALUE self, VALUE ruleset_fd) {
#ifdef __linux__
  if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
    raise_syscall_error("prctl(PR_SET_NO_NEW_PRIVS)");
  }

  long ret = ll_restrict_self(NUM2INT(ruleset_fd), 0);
  if (ret < 0) {
    raise_syscall_error("landlock_restrict_self");
  }
  return Qtrue;
#else
  errno = ENOSYS;
  raise_syscall_error("landlock_restrict_self");
#endif
}

.abi_versionObject



19
20
21
22
23
24
25
26
27
28
# File 'ext/landlock/landlock.c', line 19

static VALUE rb_ll_abi_version(VALUE self) {
  long abi = ll_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
  if (abi < 0) {
    if (errno == ENOSYS || errno == EOPNOTSUPP) {
      return INT2FIX(0);
    }
    raise_syscall_error("landlock_create_ruleset");
  }
  return LONG2NUM(abi);
}

.captureObject



35
36
37
# File 'lib/landlock.rb', line 35

def capture(...)
  Execution.capture(...)
end

.capture!Object



39
40
41
# File 'lib/landlock.rb', line 39

def capture!(...)
  Execution.capture!(...)
end

.execObject



27
28
29
# File 'lib/landlock.rb', line 27

def exec(...)
  Execution.exec(...)
end

.restrict!Object



23
24
25
# File 'lib/landlock.rb', line 23

def restrict!(...)
  Policy.restrict!(...)
end

.seccomp_deny_network!Object



124
125
126
127
128
129
130
# File 'ext/landlock/landlock.c', line 124

static VALUE rb_ll_seccomp_deny_network(VALUE self) {
  const char *error_message = "seccomp(SECCOMP_SET_MODE_FILTER)";
  if (rb_landlock_seccomp_deny_network(&error_message) != 0) {
    raise_syscall_error(error_message);
  }
  return Qtrue;
}

.spawnObject



31
32
33
# File 'lib/landlock.rb', line 31

def spawn(...)
  Execution.spawn(...)
end

.supported?Boolean

Returns:

  • (Boolean)


17
18
19
20
21
# File 'lib/landlock.rb', line 17

def supported?
  abi_version.positive?
rescue Error
  false
end