diff --git a/.build_id b/.build_id index cb7698385..7c4b30f07 100644 --- a/.build_id +++ b/.build_id @@ -1 +1 @@ -18.09.0.build20181116172621 +18.09.0.build20181116172721 diff --git a/os/packages/osctl/Gemfile b/os/packages/osctl/Gemfile index 5d3a1022b..6526f85ce 100644 --- a/os/packages/osctl/Gemfile +++ b/os/packages/osctl/Gemfile @@ -1,2 +1,2 @@ source 'https://rubygems.vpsfree.cz' -gem 'osctl', '18.09.0.build20181116172621' +gem 'osctl', '18.09.0.build20181116172721' diff --git a/os/packages/osctl/Gemfile.lock b/os/packages/osctl/Gemfile.lock index 198b6639a..e2e4b8f26 100644 --- a/os/packages/osctl/Gemfile.lock +++ b/os/packages/osctl/Gemfile.lock @@ -6,15 +6,15 @@ GEM highline (1.7.10) ipaddress (0.8.3) json (2.1.0) - libosctl (18.09.0.build20181116172621) + libosctl (18.09.0.build20181116172721) require_all (~> 2.0.0) - osctl (18.09.0.build20181116172621) + osctl (18.09.0.build20181116172721) curses gli (~> 2.17.1) highline (~> 1.7.10) ipaddress (~> 0.8.3) json - libosctl (= 18.09.0.build20181116172621) + libosctl (= 18.09.0.build20181116172721) rainbow (~> 3.0.0) require_all (~> 2.0.0) ruby-progressbar (~> 1.9.0) @@ -26,7 +26,7 @@ PLATFORMS ruby DEPENDENCIES - osctl (= 18.09.0.build20181116172621) + osctl (= 18.09.0.build20181116172721) BUNDLED WITH 1.16.3 diff --git a/os/packages/osctl/gemset.nix b/os/packages/osctl/gemset.nix index be07e5ed8..46a29b7db 100644 --- a/os/packages/osctl/gemset.nix +++ b/os/packages/osctl/gemset.nix @@ -43,19 +43,19 @@ dependencies = ["require_all"]; source = { remotes = ["https://rubygems.vpsfree.cz"]; - sha256 = "0kk7v1zafjrji9dj6frvph9qgdrqly8y8yw70xkhamdh0dcjv152"; + sha256 = "0bkj9ylx59sx8rfqks7wfj9xgilw0bwrkyprhdwhmmg3xs2dc3bx"; type = "gem"; }; - version = "18.09.0.build20181116172621"; + version = "18.09.0.build20181116172721"; }; osctl = { dependencies = ["curses" "gli" "highline" "ipaddress" "json" "libosctl" "rainbow" "require_all" "ruby-progressbar"]; source = { remotes = ["https://rubygems.vpsfree.cz"]; - sha256 = "0384n277r380bkpvvgbkzk6dzg9fvp8syzb5v7fqhlpb8y6cyqdi"; + sha256 = "0idz0vh81mhv48w5lzsqv95v4q9fgslqapn7m0vp307w6882xha6"; type = "gem"; }; - version = "18.09.0.build20181116172621"; + version = "18.09.0.build20181116172721"; }; rainbow = { source = { diff --git a/os/packages/osctld/Gemfile b/os/packages/osctld/Gemfile index 709e41219..29c408c2f 100644 --- a/os/packages/osctld/Gemfile +++ b/os/packages/osctld/Gemfile @@ -1,2 +1,2 @@ source 'https://rubygems.vpsfree.cz' -gem 'osctld', '18.09.0.build20181116172621' +gem 'osctld', '18.09.0.build20181116172721' diff --git a/os/packages/osctld/Gemfile.lock b/os/packages/osctld/Gemfile.lock index 5ff1cfd65..7d5b84974 100644 --- a/os/packages/osctld/Gemfile.lock +++ b/os/packages/osctld/Gemfile.lock @@ -6,29 +6,29 @@ GEM gli (2.17.2) ipaddress (0.8.3) json (2.1.0) - libosctl (18.09.0.build20181116172621) + libosctl (18.09.0.build20181116172721) require_all (~> 2.0.0) netlinkrb (0.18.vpsadminos.0) - osctl-repo (18.09.0.build20181116172621) + osctl-repo (18.09.0.build20181116172721) filelock gli (~> 2.17.1) json - libosctl (= 18.09.0.build20181116172621) + libosctl (= 18.09.0.build20181116172721) require_all (~> 2.0.0) - osctld (18.09.0.build20181116172621) + osctld (18.09.0.build20181116172721) concurrent-ruby (~> 1.0.5) ipaddress (~> 0.8.3) json - libosctl (= 18.09.0.build20181116172621) + libosctl (= 18.09.0.build20181116172721) netlinkrb (= 0.18.vpsadminos.0) - osctl-repo (= 18.09.0.build20181116172621) - osup (= 18.09.0.build20181116172621) + osctl-repo (= 18.09.0.build20181116172721) + osup (= 18.09.0.build20181116172721) require_all (~> 2.0.0) ruby-lxc (= 1.2.3) - osup (18.09.0.build20181116172621) + osup (18.09.0.build20181116172721) gli (~> 2.17.1) json - libosctl (= 18.09.0.build20181116172621) + libosctl (= 18.09.0.build20181116172721) require_all (~> 2.0.0) require_all (2.0.0) ruby-lxc (1.2.3) @@ -37,7 +37,7 @@ PLATFORMS ruby DEPENDENCIES - osctld (= 18.09.0.build20181116172621) + osctld (= 18.09.0.build20181116172721) BUNDLED WITH 1.16.3 diff --git a/os/packages/osctld/gemset.nix b/os/packages/osctld/gemset.nix index 263f8b2e1..87d30fe91 100644 --- a/os/packages/osctld/gemset.nix +++ b/os/packages/osctld/gemset.nix @@ -43,10 +43,10 @@ dependencies = ["require_all"]; source = { remotes = ["https://rubygems.vpsfree.cz"]; - sha256 = "0kk7v1zafjrji9dj6frvph9qgdrqly8y8yw70xkhamdh0dcjv152"; + sha256 = "0bkj9ylx59sx8rfqks7wfj9xgilw0bwrkyprhdwhmmg3xs2dc3bx"; type = "gem"; }; - version = "18.09.0.build20181116172621"; + version = "18.09.0.build20181116172721"; }; netlinkrb = { source = { @@ -60,28 +60,28 @@ dependencies = ["filelock" "gli" "json" "libosctl" "require_all"]; source = { remotes = ["https://rubygems.vpsfree.cz"]; - sha256 = "0mghxd0j6iy5b777hmbq3h0g26j10yvvg2whw3b2hn322bh6l6j1"; + sha256 = "1waz7d7cl6xmvkqihm1gf1vkkgvw0yrr45lvrkva59rsyjkh3wsy"; type = "gem"; }; - version = "18.09.0.build20181116172621"; + version = "18.09.0.build20181116172721"; }; osctld = { dependencies = ["concurrent-ruby" "ipaddress" "json" "libosctl" "netlinkrb" "osctl-repo" "osup" "require_all" "ruby-lxc"]; source = { remotes = ["https://rubygems.vpsfree.cz"]; - sha256 = "02yb1837v7kbxinl51jlxsrjgb5q8lhjig94fpmcwscsdh3rd02b"; + sha256 = "06f4a4w8mjnpbmq4yzbh9ccmhcacci9mr29dsnbzj2jjgfm3pnlm"; type = "gem"; }; - version = "18.09.0.build20181116172621"; + version = "18.09.0.build20181116172721"; }; osup = { dependencies = ["gli" "json" "libosctl" "require_all"]; source = { remotes = ["https://rubygems.vpsfree.cz"]; - sha256 = "0vj90ig2rvrwqhmrc8bkzz7nac429zqnp2fj70dq9s2h6j6mjdyv"; + sha256 = "0v2ls6d3jdd6falz6gpk9s1mzilmymb8c2kq01bn2ra25kig12fg"; type = "gem"; }; - version = "18.09.0.build20181116172621"; + version = "18.09.0.build20181116172721"; }; require_all = { source = { diff --git a/os/packages/osup/Gemfile b/os/packages/osup/Gemfile index 2f3cc114e..3611dffae 100644 --- a/os/packages/osup/Gemfile +++ b/os/packages/osup/Gemfile @@ -1,2 +1,2 @@ source 'https://rubygems.vpsfree.cz' -gem 'osup', '18.09.0.build20181116172621' +gem 'osup', '18.09.0.build20181116172721' diff --git a/os/packages/osup/Gemfile.lock b/os/packages/osup/Gemfile.lock index b4f87d0a5..a8e7b9f79 100644 --- a/os/packages/osup/Gemfile.lock +++ b/os/packages/osup/Gemfile.lock @@ -3,12 +3,12 @@ GEM specs: gli (2.17.2) json (2.1.0) - libosctl (18.09.0.build20181116172621) + libosctl (18.09.0.build20181116172721) require_all (~> 2.0.0) - osup (18.09.0.build20181116172621) + osup (18.09.0.build20181116172721) gli (~> 2.17.1) json - libosctl (= 18.09.0.build20181116172621) + libosctl (= 18.09.0.build20181116172721) require_all (~> 2.0.0) require_all (2.0.0) @@ -16,7 +16,7 @@ PLATFORMS ruby DEPENDENCIES - osup (= 18.09.0.build20181116172621) + osup (= 18.09.0.build20181116172721) BUNDLED WITH 1.16.3 diff --git a/os/packages/osup/gemset.nix b/os/packages/osup/gemset.nix index d765643b0..0642f6487 100644 --- a/os/packages/osup/gemset.nix +++ b/os/packages/osup/gemset.nix @@ -19,19 +19,19 @@ dependencies = ["require_all"]; source = { remotes = ["https://rubygems.vpsfree.cz"]; - sha256 = "0kk7v1zafjrji9dj6frvph9qgdrqly8y8yw70xkhamdh0dcjv152"; + sha256 = "0bkj9ylx59sx8rfqks7wfj9xgilw0bwrkyprhdwhmmg3xs2dc3bx"; type = "gem"; }; - version = "18.09.0.build20181116172621"; + version = "18.09.0.build20181116172721"; }; osup = { dependencies = ["gli" "json" "libosctl" "require_all"]; source = { remotes = ["https://rubygems.vpsfree.cz"]; - sha256 = "0vj90ig2rvrwqhmrc8bkzz7nac429zqnp2fj70dq9s2h6j6mjdyv"; + sha256 = "0v2ls6d3jdd6falz6gpk9s1mzilmymb8c2kq01bn2ra25kig12fg"; type = "gem"; }; - version = "18.09.0.build20181116172621"; + version = "18.09.0.build20181116172721"; }; require_all = { source = { diff --git a/os/packages/svctl/Gemfile b/os/packages/svctl/Gemfile index 1f0893c56..9d9ca8cf7 100644 --- a/os/packages/svctl/Gemfile +++ b/os/packages/svctl/Gemfile @@ -1,2 +1,2 @@ source 'https://rubygems.vpsfree.cz' -gem 'svctl', '18.09.0.build20181116172621' +gem 'svctl', '18.09.0.build20181116172721' diff --git a/os/packages/svctl/Gemfile.lock b/os/packages/svctl/Gemfile.lock index 7cc6505e2..746fcbaa5 100644 --- a/os/packages/svctl/Gemfile.lock +++ b/os/packages/svctl/Gemfile.lock @@ -2,18 +2,18 @@ GEM remote: https://rubygems.vpsfree.cz/ specs: gli (2.17.2) - libosctl (18.09.0.build20181116172621) + libosctl (18.09.0.build20181116172721) require_all (~> 2.0.0) require_all (2.0.0) - svctl (18.09.0.build20181116172621) + svctl (18.09.0.build20181116172721) gli (~> 2.17.1) - libosctl (= 18.09.0.build20181116172621) + libosctl (= 18.09.0.build20181116172721) PLATFORMS ruby DEPENDENCIES - svctl (= 18.09.0.build20181116172621) + svctl (= 18.09.0.build20181116172721) BUNDLED WITH 1.16.3 diff --git a/os/packages/svctl/gemset.nix b/os/packages/svctl/gemset.nix index dda809c50..6e2fc14da 100644 --- a/os/packages/svctl/gemset.nix +++ b/os/packages/svctl/gemset.nix @@ -11,10 +11,10 @@ dependencies = ["require_all"]; source = { remotes = ["https://rubygems.vpsfree.cz"]; - sha256 = "0kk7v1zafjrji9dj6frvph9qgdrqly8y8yw70xkhamdh0dcjv152"; + sha256 = "0bkj9ylx59sx8rfqks7wfj9xgilw0bwrkyprhdwhmmg3xs2dc3bx"; type = "gem"; }; - version = "18.09.0.build20181116172621"; + version = "18.09.0.build20181116172721"; }; require_all = { source = { @@ -28,9 +28,9 @@ dependencies = ["gli" "libosctl"]; source = { remotes = ["https://rubygems.vpsfree.cz"]; - sha256 = "1kmha18nciayy0gn1j4g884rxypws7kz62fbc06ynclx61v987md"; + sha256 = "1z5qllv8jvlc739hlcgi9qvkdsr84y6v4v0la3hlc37izsb0rf7y"; type = "gem"; }; - version = "18.09.0.build20181116172621"; + version = "18.09.0.build20181116172721"; }; } \ No newline at end of file diff --git a/osctl/lib/osctl/cli/container.rb b/osctl/lib/osctl/cli/container.rb index 1f2efa152..445544348 100644 --- a/osctl/lib/osctl/cli/container.rb +++ b/osctl/lib/osctl/cli/container.rb @@ -387,22 +387,14 @@ def console def attach require_args!('id') - shell = osctld_call( + cmd = osctld_call( :ct_attach, id: args[0], pool: gopts[:pool], - user_shell: opts['user-shell'] + user_shell: opts['user-shell'], ) - pid = Process.fork do - shell[:env].each do |k, v| - ENV[k.to_s] = v - end - - Process.exec(*shell[:cmd]) - end - - Process.wait(pid) + handle_ct_attach(cmd) end def exec @@ -451,15 +443,7 @@ def su require_args!('id') cmd = osctld_call(:ct_su, id: args[0], pool: gopts[:pool]) - pid = Process.fork do - cmd[:env].each do |k, v| - ENV[k.to_s] = v - end - - Process.exec(*cmd[:cmd]) - end - - Process.wait(pid) + handle_ct_attach(cmd) end def set_autostart @@ -1278,5 +1262,28 @@ def handle_exec_response(c) raise GLI::CustomExit.new('executed command failed', resp[:exitstatus]) end end + + def handle_ct_attach(cmd) + f = Tempfile.create(['osctl-ct-attach-settings', '.json'], '/tmp') + f.puts(cmd[:settings].to_json) + f.close + + pid = Process.fork do + cmd[:env].each do |k, v| + ENV[k.to_s] = v + end + + Process.exec(cmd[:cmd], f.path, '--', *cmd[:args]) + end + + Process.wait(pid) + + ensure + begin + f && File.unlink(f.path) + rescue Errno::ENOENT + # pass + end + end end end diff --git a/osctld/lib/osctld/cli/exec.rb b/osctld/lib/osctld/cli/exec.rb index c0488f277..2435b51ed 100644 --- a/osctld/lib/osctld/cli/exec.rb +++ b/osctld/lib/osctld/cli/exec.rb @@ -1,10 +1,24 @@ +require 'libosctl' + module OsCtld class Cli::Exec def self.run - sysuser, ugid, homedir, cgroup, *args = ARGV + if ARGV.size < 3 || ARGV[1] != '--' + warn "Usage: -- [arguments...]" + exit(false) + end + + OsCtl::Lib::Logger.setup(:none) + cfg = JSON.parse(File.read(ARGV[0]), symbolize_names: true) - SwitchUser.switch_to(sysuser, ugid.to_i, homedir, cgroup) - Process.exec(*args) + SwitchUser.apply_prlimits(Process.pid, cfg[:prlimits]) + SwitchUser.switch_to( + cfg[:user], + cfg[:ugid], + cfg[:homedir], + cfg[:cgroup_path] + ) + Process.exec(*ARGV[2..-1]) end end end diff --git a/osctld/lib/osctld/commands/container/start.rb b/osctld/lib/osctld/commands/container/start.rb index ea0256ced..8673a997b 100644 --- a/osctld/lib/osctld/commands/container/start.rb +++ b/osctld/lib/osctld/commands/container/start.rb @@ -120,13 +120,13 @@ def start_now(ct) ] progress('Starting container') - pid = Process.fork do - SwitchUser.switch_to( - ct.user.sysusername, - ct.user.ugid, - ct.user.homedir, - ct.cgroup_path - ) + pid = SwitchUser.fork_and_switch_to( + ct.user.sysusername, + ct.user.ugid, + ct.user.homedir, + ct.cgroup_path, + prlimits: ct.prlimits.export, + ) do Process.spawn(*cmd, pgroup: true, in: :close, out: :close, err: :close) end @@ -138,7 +138,11 @@ def start_now(ct) log(:warn, ct, "Unable to connect to tty0") end - Process.wait(pid) + begin + Process.wait(pid) + rescue Errno::ECHILD + log(:warn, 'sad panda') + end :wait end diff --git a/osctld/lib/osctld/prlimits/manager.rb b/osctld/lib/osctld/prlimits/manager.rb index 91a4a3cf0..aef32a2ce 100644 --- a/osctld/lib/osctld/prlimits/manager.rb +++ b/osctld/lib/osctld/prlimits/manager.rb @@ -66,6 +66,12 @@ def dump inclusively { Hash[prlimits.map { |k, v| [k, v.dump] }] } end + def export + inclusively do + Hash[prlimits.map { |k, v| [k, v.export] }] + end + end + def dup(new_ct) ret = super() ret.instance_variable_set('@ct', new_ct) diff --git a/osctld/lib/osctld/switch_user.rb b/osctld/lib/osctld/switch_user.rb index 48bb5c6e5..f1a9d3195 100644 --- a/osctld/lib/osctld/switch_user.rb +++ b/osctld/lib/osctld/switch_user.rb @@ -1,6 +1,48 @@ +require 'libosctl' + module OsCtld module SwitchUser - def self.switch_to(sysuser, ugid, homedir, cgroup_path, chown_cgroups: true) + include OsCtl::Lib::Utils::Log + extend OsCtl::Lib::Utils::System + + # Fork a process running as unprivileged user + # @param sysuser [String] + # @param ugid [Integer] + # @param homedir [String] + # @param cgroup_path [String] + # @param opts [Hash] options + # @option opts [Boolean] :chown_cgroups (true) + # @option opts [Hash] :prlimits + def self.fork_and_switch_to(sysuser, ugid, homedir, cgroup_path, opts = {}, &block) + r, w = IO.pipe + + pid = Process.fork do + w.close + switch_to(sysuser, ugid, homedir, cgroup_path, opts) + + msg = r.readline.strip + r.close + + if msg == 'ready' + block.call + else + exit(false) + end + end + + r.close + + apply_prlimits(pid, opts[:prlimits]) if opts[:prlimits] + + w.puts('ready') + w.close + pid + end + + # Switch the current process to an unprivileged user + def self.switch_to(sysuser, ugid, homedir, cgroup_path, opts = {}) + chown_cgroups = opts.has_key?(:chown_cgroups) ? opts[:chown_cgroups] : true + # Environment ENV.delete('XDG_SESSION_ID') ENV.delete('XDG_RUNTIME_DIR') @@ -24,6 +66,8 @@ def self.switch_to(sysuser, ugid, homedir, cgroup_path, chown_cgroups: true) Process::Sys.setuid(ugid) end + # Switch the current process to an unprivileged users, but do not change + # cgroups. def self.switch_to_system(sysuser, uid, gid, homedir) # Environment ENV.delete('XDG_SESSION_ID') @@ -37,5 +81,16 @@ def self.switch_to_system(sysuser, uid, gid, homedir) Process::Sys.setgid(gid) Process::Sys.setuid(uid) end + + # Apply process resource limits + # @param pid [Integer] + # @param prlimits [Hash] + def self.apply_prlimits(pid, prlimits) + args = prlimits.map do |name, limit| + "--#{name}=#{limit[:soft]}:#{limit[:hard]}" + end + + syscmd("prlimit --pid #{pid} #{args.join(' ')}") + end end end diff --git a/osctld/lib/osctld/utils/switch_user.rb b/osctld/lib/osctld/utils/switch_user.rb index af3ad6842..b43615744 100644 --- a/osctld/lib/osctld/utils/switch_user.rb +++ b/osctld/lib/osctld/utils/switch_user.rb @@ -6,15 +6,15 @@ module Utils::SwitchUser def ct_control(ct, cmd, opts = {}) r, w = IO.pipe - pid = Process.fork do + pid = SwitchUser.fork_and_switch_to( + ct.user.sysusername, + ct.user.ugid, + ct.user.homedir, + ct.cgroup_path, + prlimits: ct.prlimits.export, + ) do r.close - SwitchUser.switch_to( - ct.user.sysusername, - ct.user.ugid, - ct.user.homedir, - ct.cgroup_path - ) ret = SwitchUser::ContainerControl.run(cmd, opts, { lxc_home: ct.lxc_home, user_home: ct.user.homedir, @@ -40,11 +40,16 @@ def ct_control(ct, cmd, opts = {}) def ct_attach(ct, *args) { - cmd: [ - ::OsCtld.bin('osctld-ct-exec'), ct.user.sysusername, ct.user.ugid.to_s, - ct.user.homedir, ct.cgroup_path - ] + args.map(&:to_s), - env: Hash[ENV.select { |k,_v| k.start_with?('BUNDLE') || k.start_with?('GEM') }] + cmd: ::OsCtld.bin('osctld-ct-exec'), + args: args.map(&:to_s), + env: Hash[ENV.select { |k,_v| k.start_with?('BUNDLE') || k.start_with?('GEM') }], + settings: { + user: ct.user.sysusername, + ugid: ct.user.ugid, + homedir: ct.user.homedir, + cgroup_path: ct.cgroup_path, + prlimits: ct.prlimits.export, + }, } end @@ -191,7 +196,7 @@ def ct_syscmd(ct, cmd, opts = {}) out_r.close if !ret[:status] - raise OsCtld::SystemCommandFailed "Command '#{cmd}' within CT #{ct.id} failed" + raise OsCtld::SystemCommandFailed, "Command '#{cmd}' within CT #{ct.id} failed" elsif ret[:output][:exitstatus] != 0 && \ opts[:valid_rcs] != :all && \